What's hot ? (and I mean really ...) - scroll down for more
1).  Code Templating - advanced usage of delegates & generics: my slides & demos are available for download! CodeProject article is also available.

2).  My series "TDD in the eyes of a simpleminded" is in progress(including code!): preface, part1, part2, Q&A 1, Manual Stub .vs. Mock Stub

3).  TDD Workshop: SeeCompass v0.1 and v0.2 are out.
# Friday, September 01, 2006

A few months ago, I lectured before Microsoft C#/C++ User Group about advance usage of delegates, generics and anonymous methods. The audience was great and I got really nice feedback. I was talking with my good friend Pavel a few days ago about lecturing in general and he made an excellent point: I should have done more "rounds" with this lecture. This could have been good experience for me to learn from the feedback I got and use it into practice before different audience. So, would you like to hear me present for ~1.5 hours about those topics (after "work hours", say 17:30+) in your "home"(=working place)? All I need is a projector and a working air conditioner(I don't have 3 built-in fan as my computer have, unfortunately). You can look at the lecture syllabus here and there are code samples and presentation(ppt) file of the entire thing right here. Of course, there should be a minimum number of people attending as I don't want to drive for an hour just to talk with 2 people (actually, I do, but this won't be very productive and bad usage of my time, so I'm sorry folks).

Minimum knowledge required to participate: good understanding of .Net 1.1 is a must; .Net 2.0 - bonus.
Lecture ("MS")Level: 400. (technical hardcore stuff, no SOA will be presented, I promise).


Drop a comment or send me an email (oren dot ellenbogen at gmail dot com) and we'll set a date.

Posted by Oren Ellenbogen 
01/09/2006 12:44, Israel time UTC+03:00,     Comments [0]  | 
# Thursday, August 31, 2006

Let it be no mistakes, I'm not a great interviewer and definitely not an experienced one but I've got really strong opinions about the way interviews should go and the questions "style" that should help you screen your candidates. It's not only "good" .vs. "evil" questions that I want to talk about here. It's the attitude and the fun of the process. Yes, you read it just right: interviews should be FUN.

Money is an interview's killer.

Money is ego. ego turns to hostility. hostility lead to bad questions. bad questions screen the wrong people. bad people is bad code. bad code is no fun. no fun is plain boring. boring is death. death is no fun. infinite loop. break;

I went to a few places after leaving my last company. What's the first thing you get after entering the building? Right! A form to fill! In some of the places, there is a cute line "desired pay: ______" in between the other lines of personal information. So you write something up (you can always leave it blank, but it will be the first question you'll be asked in those places, so save your breath), fill the rest of the form and returning it. Nice. So far, have I managed to hit 95% of your interviews experiences?

Next step. "Please sir(in Hebrew it sounds a lot WORSE, it's something like "this way man, hurry", but with a big smile), meet our HR girl(always a girl, and 99% of the time, she takes your mind out of the job)". You go in. Talk for a bit and than it arise: "Wow! X money! really?! maybe you slipped a 0 at the end ? no? oh... That is really a lot! are your sure? You do? oh....". I just hate it. I can't help to think:"Do you know what is a variable that you speak about money! I can be the next Don Box and you yabbing about money!?!" Yes, it's bullet number 1 in the "negotiations for dummies" book (I wonder if such a thing exists). So you explain and you argue and if you're convincing you continue. "Please sir ("faster you killer-budget" in Hebrew, and that smile again), lets meet our Team Leader\CTO". And what is the first thing that happens there? You know the answer ? Wow? Who told you? Oh.... you've been there haven't you?

"Hey [cute girl's name], Let me see the form he filled.... hmmm... I see.... "...
And of course, he is thinking inside "what?! that's is double than what I earn! and I'm the frickin' best in the industry!... Ok bastard, bring it on, you say you worth it ah? let's see..."

Kiss your interview goodbye. You can be Brad Abrams or Juval Lowy. It doesn't really matter. You are at war and sadly you can't win this one. You are either stupid to get what you want and arrogant to even request it or too arrogant for saying the right answers all the time.

One of the reason, among 1000 more reasons, that I've chosen Mercury as my new home was for that reason. We didn't talk about $$$ until the far end (after 2.5 technical interviews, 1 management interview and 1 HR interview). They were really interested in ME, not it the paycheck I request.

A very good comment will be "But what if we can't pay him that much? Isn't it a waste of time to go so far and at the end say to him that he requests more than we can offer". I'm with you guys. Every company has their budget. Limits should be set, no matter(?) how good this person is. But at least don't let the interviewer see anything related to money! It shouldn't bother him! And I've seen this pattern in so many places. What a shame. It happened to me in two places and I immediately "turned off" at the beginning. I knew that I'm not going to be a part of such a place and did my best to show the "better than you and still earn less" that tried to play it rough that I can raise to his challenge. After all, I want to have fun in interviews, no matter how bad it goes. My tip for you is: Let it be known, good places do exists, and good guys have a room there, just be patient and have the confidence in your abilities. Don't be afraid of bad interviews, it's good experience and it can be fun. Play your best game.


State of mind.

This is tricky, read the next paragraph and than think about it with your eyes closed.

Imagine that you are at work now, and doing the stuff you like so much. Think about the things you made that make you happy and proud, about the people you met that made you a better person and a better worker. Consider how in one hour you managed to write 10 hours work. Think about the happy customers you made. Smile. Now open your eyes.

What comes through your mind? What made this all possible? I don't know about you, but my feelings were:

1. The people. What a bunch of great people. I love them all. They enrich me and make my time better. This is my 2nd home.
2. The challenge. So many great things to think about during the day, so many challenges to figure out, so many small wins in a long but glorious battle. so many loses, but so many insights.
3. Pride.
My code means something, I 100% believe in it. I'm proud of my code and I'm working hard to worth the respect from my friends at work.

this is just from the top of my mind, no re-write here. this is the set of mind you need to set for your interviewee in order to achieve "good interview" experience. You're questions must let him understand that he could be a part of a great bunch of people, that challenges will be there for him and so is help and friendship. You should let him know that HIS thoughts and HIS answers and questions are important in this interview. You should let him know that pride is there for reach. We all love a good word here and there. You should let him understand that this can be his new home.

Remember the feeling you had after you open your eyes. That should be the interviewee's feeling at the beginning of the interview and during it. I explicitly mentioned the word "beginning" as people are nervous at the beginning, so it's extremely important that you'll start slow and let the interviewee know that you want to get to know HIM, no matter how bad\good he\she* answers your questions.

Good .vs. Bad questions.

These are a few questions that only the "best" (my best) companies asked me and I feel that it's important to share them with you. Good questions are question with space. think about driving to your work at morning. You wake up and listen to the radio: "Road 5 is jammed between this and that". "Oh... So I would probably drive in road number 2 and cut to road number 4... It's longer(length) but faster". Roads give you space to decide. Usually. Good questions are the same. The (interviewee's)answer is irrelevant (you can always ask someone how to get to ThatIsTheOne st. if you nearby) as long as his mind is set for the right questions and answers. If he is not in the right path, help him out a bit(to an extent of course). For the interviewers among you, please, If there is one thing I would like you to take from this post is this: You are interviewing someone else, not yourself. That is why good questions allow the interviewee the space to let the interviewer konw what he thinks, may it be a complete joke. If it will be supplied with good reasons, than damn - I want this guy on my team. He is creative! I would not think about this option by myself.

Good personal questions I love to hear to start with:

"Tell me about an API of some class you wrote. What was the purpose? Is it(the class) good? how do you know? Would you change it now if you could, if so - why? Are you proud of that class? Tell me why."

"Do you read books\articles in the subject(any subject that you think is fit)? if so, tell me about a thing or to you picked up and how did it affect your work? tell me about something you found in one resource that you couldn't find in any other place. Why do you think it's only there? Do you write things yourself? Why? Do you enjoy it?"

"Do you love doing what you're doing? Tell me about it, why do you love it so much? do you take things "to bed"? do you love to think about work after work hours? do you think that this is a good thing?"

"Did you made a change in the last place you worked at? If so - tell me how. Are you proud of that change? Do you like to change places you work at? Did you get recognition about it? If so - did you enjoy it?"

"Why do you think you are good at what you're doing? Please give me examples of what makes you more special than the "next guy". What are you really good at? Do you think that it's possible for you to get better at it? Can you teach others? do you enjoy it?"


This should let you a good look at the inside of the interviewee, about his true nature, his true character. There is no fear here. No mistakes. Every answer is OK. This is simply how he\she is wired. I would always start with these questions as they will set the right mood. After talking about his personality and a little digging through his code, we can decide that the personality is not suited for the team and tell him goodbye or we can go a little deeper into the technical stuff.

Technical questions style I love to hear:

I love thinking questions (surprise surprise). Show a given algorithm and let the interviewee tell you what he believe that this code does. Talk about general syntax and basic patterns and see if he follows. Don't let him know if he is correct or not at the beginning, no fun in that.

Try to focus on algorithms instead of hardcore-"who cares?!" questions. Remember, it's all about space. If there is no place for multiple choices and multiple answers, you are doing it wrong. Always try to give hard questions in between, something nice that will show the interviewee that we are having fun, but we also mean business. Try to challenge and guide him for the solution. Always, always, let him get to the solution by himself, help him only with clues until he figure it out.


To recap, I am a big fan of Logic\Tree\Recursive solutions\Object Oriented\Design Patterns questions rather than "what this line of code will do: int i=10; i = i++*++i;". There are variety of questions out there, pick the ones you like the most and have a nice list of them so you can change the questions from time to time.

People make the company. Your interview should leave you with the best people and those (best)people should want to work for you after that interview. Invest time teaching your employees how to make interviews for others(if they need to have that skill). Hiring is the company's core, no less.


* I'm sorry if I wrote he instead of he\she somewhere in this post. We are all son of God(are we?).
Posted by Oren Ellenbogen 
31/08/2006 02:56, Israel time UTC+03:00,     Comments [3]  | 
# Wednesday, August 16, 2006

This (very)little beauty is getting along quite nicely. I'm going to work quite a bit on it in the following days so keep tracking...

source:

Lnbogen.SeeCompass_v0.2.zip (205.34 KB)

.NET | TDD
Posted by Oren Ellenbogen 
16/08/2006 12:08, Israel time UTC+03:00,     Comments [0]  | 
# Tuesday, August 15, 2006

Hey guys & gals,

After a few intense interviews and a great time talking about things I just love (code, code, code), I did it.  

I'm very proud to inform you that I've join Mercury as a programmer at the Performance Center product

 

 mercury_logo.gif

" Mercury Performance Centerâ„¢ is an enterprise-class application performance testing platform. It provides a complete, on-demand optimization solution for your application performance process, integrating load testing with diagnostics... Mercury Performance Center is the platform on which you can base your quality initiative. Mercury Performance Center delivers a 24x7, globally accessible and shareable pool of load testing resources to minimize the total cost of load testing in the enterprise "


Man, I'm so eager to start !!!

Posted by Oren Ellenbogen 
15/08/2006 10:30, Israel time UTC+03:00,     Comments [7]  | 
# Friday, August 11, 2006

After sitting with Roy during our last meeting, we managed to write some neat tests and code and the SEE(Simple Expression Engine) infrastructure started to crystallized. Roy even managed to refactor the name of my infrastructure and brought a brilliant suffix - SeeCompass. SEE is like a compass that helps you navigate in the ocean of data; man, I think that the web-bully will not be happy with this phrase.

If you don't remember what is SeeCompass all about read this post.

Source:

Lnbogen.SeeCompass.zip (156.39 KB)

How to start:

1. Open the solution (I know, revolutionary).
2. Open Lnbogen.SeeCompass.Framework.Tests project.
3. Open the file FilterExpressionTests.cs under FilterExpression directory.
4. Jump to the test Build_EmptyFilter_EmptyString (remember the method name structure: MethodToTest_MethodState_ExpectedResult).
5. Move on to the next test, one at a time.


I know, it can be a little hard to grasp the "entire picture" at the moment, but the tests should give you a good pick at the future.
As always, I left one failing test which will lead to a very drastic refactoring (BetweenExpression should have more than one parameter) - this will give me an entry point to continue.

.NET | TDD
Posted by Oren Ellenbogen 
11/08/2006 05:59, Israel time UTC+03:00,     Comments [0]  | 
# Tuesday, August 08, 2006

While I was sitting in McDonald's today, eating some junk-burger, I looked across my table and noticed a beautiful girl. Man, she was something else. A true beauty. I looked at her walking to another table, 20 fit from my table and sitting down. In front of her was a woman. Try to guess on your own, how does the woman look like:

a. A true super-hot-mama, even more beautiful than her daughter(?).
b. Good looking woman.
c. An OK woman.
d. My GOD! What a monster ?!

I'll let you ponder for a few seconds...

Now that I'm sure you've got the right answer, have you ever thought about these questions:

In 15 years, will this beautiful girl will look like her mother(?) ?
Is there any chance that god is just messing with my head and she'll be a fine woman even if her mother(?) is a....
What about statistics? Anyone knows what are the odds for a beautiful girl to become a beautiful woman ?

And of course, a sick question to recap this self-debate:

What does her father think about this situation ?

For some strange reason, I started to think about applications. I know, I'm sick, no doubt, but bare with me: How many small beautiful applications you developed, become a monstrous Enterprise Applications that are eating their programmers for breakfast ? Go back to my questions and replace the word "girl" with "small application" and the word "mother" with "Enterprise Application". I think that this symptom of "growing organism" exists in developing real applications and we encounter it every day when we maintain legacy code or patching-up a system that shouldn't survive the prototype phase.

I guess that just like in life, with good monitoring of our state and good activities, this transformation, this growing, could still be graceful.

I just wonder what are the odds...

Design | Life
Posted by Oren Ellenbogen 
08/08/2006 07:34, Israel time UTC+03:00,     Comments [4]  | 
# Tuesday, July 25, 2006

Our SQLink R&D department was merged with Aman Computers.

At the moment, I'm still not sure if my place is at Aman although I've got a very nice offer from them and it seems like a very nice place to be at.

I'm looking for a place with (my ideal, I'm a realistic guy though):

  1. Many people who love coding, read about it, write about it and lecture about it.
  2. Building Enterprise applications.
  3. Using the newest technology (.Net 2.0+)
  4. A place that loves to teach his employees, to set goals and to push you through crises.
  5. Provide me the stage to show what I am capable of, where managers trying to bring out the best from their people.
  6. Organic teams. I would like to be one of a great team of people. I work better with other people around me and I love sharing my thoughts and teaching others.
  7. Agile methodologies.
If your place is looking for a passionate-for-code guy, someone that can upgrade the surrounding and contribute to the "team's glue", I would be glad if you'll contact me via email (oren dot ellenbogen AT gmail dot com) so we can chat. In addition, I'm *interested* in relocation(especially in the US, I've got a citizenship(+visa\green-card)) as it always been a dream of mine.
Posted by Oren Ellenbogen 
25/07/2006 11:55, Israel time UTC+03:00,     Comments [9]  | 
# Sunday, July 23, 2006

After talking about testing inner behavior inside a method, it is time to make some observations about the code and answer a lot of *why* questions. I must give credit to Shani, one of a great bunch of people I worked with during my service and a very good friend of mine. He asked me some hard questions that bother every TDD newbie or a TDD wannabee as myself (raise your hand if you're one of them.. higher!). Now I'll take my shot and reply in detail.

Question 1: Is it worth spending time on tests for simple methods ?

I've talked about "code little, think and code little" and why we need it in my last post "TDD in the eyes of a simpleminded: Part 1 - The NameResolver". One of the great comments I received was "You spent more time on the tests than on the (simple)method itself. Isn't it time consuming? You've just talked about focus on deadlines in your preface..". From my experience, the easy, 5 minutes, methods are the one you spend most the time thinking about(relatively). You keep saying to yourself "this is so easy... such a shame" and spend a full hour thinking about a various of problems that can happen and various solution to handle them: " Maybe I'll use array? no... maybe List<int>? no... Oh! Yes! I'll use an interface! I'll call it IMyCollection and make a nice method in it named "AISolveProblem(params object[] anyThing)" ". This all process take place before you even write your first line of code! It's a 5 minutes, even-a-lame-monkey-can-do-it method. You don't want to look stupid and leave "open holes" right? So you waste a great deal of time over-thinking the irrelevant. Classic.

Sure, TDD can be costly (depends on a few variables), but it makes you code along the challenges(tests) and always challenge you to come up with the simplest solution. minimum effort. It prevents you from thinking too much upfront; You don't have to think too much about avoiding potential bugs, just write test cases and make them green. Don't have red tests ? Your code is ready for production. Again, I'm not going to address the big questions like "when, how and how much?" at the moment and I'm not saying that you should TDD the entire application. I'm just stating a point here. Keep your head open for a new way of coding. That's the best I can request from you and that is what I'm doing now, during the learning process with Roy.

Question 2: Changing the class because of our tests, is that a good thing ?

I think that if the time comes and you need to change the way your class looks or behaves just because your tests become hard to write, you should lay back and smile. That means that the process of TDD just helped you design your code better. If the test was too hard to write, you must be doing some uncomfortable API (usability warning) or maybe you've put too much knowledge in one place and created a GOD(do-it-all) method\class. Having 5+ dependencies in one class should make you stop and rethink about the way you work right? testing actually makes you write the API before you actually write the implementation itself. If the early API sketching is too hard, just think about the poor programmer which will have to work with your API, about yourself (if it's hard to test it must be hard to write) and even better - the miserable programmer which will have to take over your code and maintain it later on.

Question 3: Injecting concrete implementation can lead to incorrect usages! Why do we need it?

Let me start by relaxing you - injecting concrete implementation of an interface *can* lead to serious bugs and malfunctions in your application. Let me repeat it. By providing the programmer the ability to "inject" his implementation for the class dependencies he can cause the application to die. It might even cost you your job.

Relaxed?

No? But you should be! I've just demonstrated the worst case scenario and if we'll think about it together, the world is not over and you and I still got our jobs. Programmers can make mistakes while using any API. Damn it, I've asked my boss for hiring machines but he keeps bringing people who actually use their head. You see, I don't trust the "next guy" no more than you do, but I realize that I can take care of the low-level (in Hebrew: BARZELIM) API and still provide the high-level API which will help the idiot(brighter than me) next guy a simple method for any complex scenario. We need an option to inject our dependencies as we don't want to couple our classes together.

We want the option to replace one concrete implementation with another (fake one, for example). This doesn't prevent me from developing some nice & simple API that will wrap the all thing up and inject the dependencies I want.

Question 4: Writing tests can lead to bugs in those tests? should I write tests for my test?

Sure you do! you have to test your tests, and then test those tests and than test the latter one just until you will run out of RAM, space on your HD or energy.

Writing code can lead to additional bugs to your system just as living can cause death(No research shows it does by the way). We can minimize the chance of bugs in our tests by following some simple rules:

  1. The test should be obvious by it's name (I follows Roy's tip: MethodName_State_ExpectedResult).
  2. A test should check one method only.
  3. A test should Assert only once.
  4. Writing new tests should be easy and take no more than a few lines (few != 50).

I wrote "should" and not "must" as we should eat healthy food, it's not a must (unless you eat burgers 24-7, in that case, you are a goner). You should come up with the rules that fit you and your team. You can enforce them to keep a follow a few rules just to create a standard you all(or just you, no one said we are living in a democracy) agree on.

TDD
Posted by Oren Ellenbogen 
23/07/2006 12:07, Israel time UTC+03:00,     Comments [2]  | 
# Saturday, July 22, 2006

I've attached the code I demonstrated so far to the original posts so you could play with the code a little and understand how it all connects (if code samples in my posts were not clear enough).

Requirements:

  • Nunit 2.2.8
  • TestDriven.Net (this is actually an extra, but give yourself a break and download this gold)
  • Visual Studio .Net 2005

Code (inside the posts):

TDD in the eyes of a simpleminded: Part 1 - The NameResolver

TDD in the eyes of a simpleminded: Part 2 - Testing inner behavior

Have fun !


p.s:
in every additional post I'll publish about "TDD in the eyes of a simpleminded" I will upload the code.

TDD
Posted by Oren Ellenbogen 
22/07/2006 11:22, Israel time UTC+03:00,     Comments [0]  | 
# Friday, July 21, 2006

code:

OrenTDDWorkShop_Part2.zip (44.06 KB)

If you haven't read about the NameResolver at my previous post(TDD: Part1), now is the time as I'm going to take this class and use it later on in this post.

We are ready to take the next step and talk about "playing a doctor". Imagine this picture: you are a doctor looking for a gun's bullet in one of your patient's stomach. He was shot a few moments ago. "One bullet to the chest" the nurse inform you. It's all messy and you can't find the bullet. You need to clear the blood, move the organs, going micro-level on the details so you don't hit a vein on the way. Every mistake is a "welcome-better-place" for your patient and you don't want this on your conscience do you ?

While developing applications, we are facing a world of "playing doctors". We need to change inner behaviors in our code or others code. We want the application to live the surgery and we realize that the smallest change in a "core method" can make the application bleed all over the place without even noticing it. Every click on the keyboard raises our heart rate. But how can we check inner code inside a method? How can we get a micro-level look at the method's internals, at the stomach of our tested object ?

This is what I'm going to address at this post. But before we see solutions, let's talk about the client's requirements so we'll have something to play with.

Client's requirements v1.0:

A class named HotelReservationService is required. In it, a method named ReserveRoomForTwo that receive the couple's name in the format "[man] and [wife] [family name]". The method will reserve the room and name the room under the family's name. The method will return ReservationInformation object with the details (room, floor number & status).

  • If the received couple's name is empty ("") - Throw an exception with "The couple name must be supplied".
  • If the hotel don't have available rooms, the returned ReservationInformation should hold a status of "NoRoomAvailable".
  • if the everything goes well, the method should return a ReservationInformation filled with the floor number, room number, reservedFor (filled with the name of the family) and status of "RoomReserved".


Simple solution, without TDD:

public class HotelReservationsService
{
   public ReservationInformation ReserveRoomForTwo(string coupleName)
   {
      // .... some checks ...

      NameResolver resolver = new NameResolver();
      string familyName = resolver.GetFamilyName(coupleName);

      HotelRepository repository = new HotelRepository();
      return repository.
ReserveRoom(2, familyName); // 2 = two people
   }
}

As you can notice, we have 2 dependencies here: NameResolver and HotelRepository. We need to test ReserveRoomForTwo but how can we do it ? the results of our tests for this method are coupled with the implementation of NameResolver and HotelRepository. If they have bugs in it, it will certainly affect our tests and therefore making them *unreliable*. What's the point in writing tests if you doubt that a green(successful) result might actually be red(fail) under the surface due to an inner bug in one of the dependencies?

Keeping with my metaphor, the dependencies are the blood and organs laying around and making it harder for you to find the bullet, may it be a bug or a new feature. You need to clear(isolate) the area so things will be easier to see. You need a green light you can actually *trust*.

We'll need to use Dependency Inversion Principle(DIP) to isolate our tested class from the actual implementation of its dependencies.

Our goal:

Inject dummy object instead of the dependencies and make them "act like" everything is good\bad according to our need. Will use DIP to make it happen.

We don't want to test NameResolver nor HotelRepository. We assume that they are bugs-free. We need to test one and only one class at a time. Our goal is to test the interaction between our tested class to its dependencies.


Let's use TDD to solve the client's requirements:

Say hello to your new friend: geek, this is Stub; Stub, this is geek. Come on, don't be shy, give him a hug, he will serve you well in time. Feel nice and fuzzy ? great, now let's TDD this baby. Oh, and don't worry, if you don't understand the concept of "Stub" yet, everything will be clearer in a few moments. Just think about Stub as a dummy object.

We start with? Come on! I want to hear it from you! we'll start with a Create method!

[TestFixture]
public class HotelReservationsServiceTests
{
   [Test]
   public void Create()
   {
      HotelReservationsService service = new HotelReservationsService();
      Assert.IsNotNull(service);
   }
}

As usual, in order to compile this method we'll need to create an empty class:

public class HotelReservationsService
{
}

Let's run the test - 1 passed, 0 failed. Good.

Let's write our second test and look to our requirements:

[Test]
[ExpectedException(typeof(ArgumentException), "The couple name must be supplied")]
public void ReserveRoomForTwo_EmptyCoupleName_ThrowEmptyCoupleNameException()
{
   HotelReservationsService service = new HotelReservationsService();
   ReservationInformation info = service.ReserveRoomForTwo(string.Empty);

   // We've got nothing to assert. We expect an exception to raise.
}

This will not compile as we don't have ReserveRoomForTwo method nor ReservationInformation class. Let's write them down:

public class ReservationInformation
{
   private int m_roomNumber;
   private int m_floorNumber;
   private string m_reservedFor;
   private string m_status;

   public ReservationInformation(string reservedFor, int floorNumber, int roomNumber, string status)
   {
      ReservedFor = reservedFor;
      FloorNumber = floorNumber;
      RoomNumber = roomNumber;
      Status = status;
   }

   public string ReservedFor
   {
      ... get & set ...
   }

   public int FloorNumber
   {
      ... get & set ...
   }

   public int RoomNumber
   {
      ... get & set ...
   }

   public string Status
   {
      ... get & set ...
   }
}

Now let's add the ReserveRoomForTwo method to our HotelReservationsService:

public ReservationInformation ReserveRoomForTwo(string coupleName)
{
   throw new NotImplementedException("");
}

Compile. Run tests - 1 passed, 1 failed. Our test expect for ArgumentException with a specific message but we throw NotImplementedException. Let's fix it.

public ReservationInformation ReserveRoomForTwo(string coupleName)
{
   throw new ArgumentException("The couple name must be supplied");
}

Run tests - 2 passed, 0 failed. Good.

Time for refactoring our tests as we create the HotelReservationsService in two methods. This time, instead of creating a "FactoryMethod" named GetNewHotelReservationsServer like we did in our NameResolver tests, let's use the [Setup] attribute.

[TestFixture]
public class HotelReservationsServiceTests
{
   HotelReservationsService service;

   [SetUp]
   public void SetupTest()
   {
      service = new HotelReservationsService();
   }

   [Test]
   public void Create()
   {
      Assert.IsNotNull(service);
   }

   [Test]
   [ExpectedException(typeof(ArgumentException), "The couple name must be supplied")]
   public void ReserveRoomForTwo_EmptyCoupleName_ThrowEmptyCoupleNameException()
   {
      ReservationInformation info = service.ReserveRoomForTwo(string.Empty);
      // We've got nothing to assert. We expect an exception to raise.
   }
}

Let's continue to check our requirements: We want to represent a case where the provided couple's name is good but the hotel is full.

[Test]
public void ReserveRoomForTwo_HotelIsFull_ReservationInformationWithStatusNoRoomAvailable()
{
   // We create a dummy repository and set the values we want it to return.
   FakeHotelRepository repository = new FakeHotelRepository();
   repository.StatusToReturn = "NoRoomAvailable";
   service.SetRepository(repository); // Important! set the fake repository in the service.

   
string
goodCoupleName = "man and woman FamilyName";
   ReservationInformation info = service.ReserveRoomForTwo(goodCoupleName);

   Assert.AreEqual("NoRoomAvailable", info.Status);
}

Run tests - 2 passed, 1 failed(our last one).

We get an exception here which is obvious (we hard-coded "throw new ArgumentException...") instead of a status "NoRoomAvailable". Notice that we are create a FakeHotelRepository and injecting it to our tested class. The purpose is to create a repository which will always return "NoRoomAvailable". We want to test the *interaction* between our ReserveRoomForTwo method in our tested object to the repository. This can look bizarre but let's remember that we want to test only our ReserveRoomForTwo method and not the repository class.

Let's make our test green. minimum effort.

We need some sort of HotelRepository in order to know if the hotel is full. At the moment, we don't need the NameResolver as we are looking for the status only. Let's define an interface named IHotelRepository.

public interface IHotelRepository
{
   ReservationInformation ReserveRoom(int capacity, string familyName);
}

This time we need to build the "basics" so the minimum effort will be a little more than we use to, but that's OK as we'll do it only once. Let's add this code to our HotelReservationsService class:

private IHotelRepository m_repository;

private IHotelRepository Repostiory
{
   get 
   { 
      if (m_repository == null)
         throw new ArgumentNullException("Repostiory", "Repository must be set before being used");

      return m_repository; 
   }
   set { m_repository = value;}
   }
}

public void SetRepository(IHotelRepository repository)
{
   Repostiory = repository;
}

Now we can use the repository in our ReserveRoomForTwo method:

public ReservationInformation ReserveRoomForTwo(string coupleName)
{
   if (coupleName == string.Empty)
      throw new ArgumentException("The couple name must be supplied");
   else
   {
      ReservationInformation info = this.Repostiory.ReserveRoom(2, coupleName);
      return info; // just so we'll remember that we return ReservationInformation.
   }
}

We call the repository and ask it to save a room for the received couple. All we left with is to create the FakeHotelRepository object:

internal class FakeHotelRepository : IHotelRepository
{
   public string ReservedForToReturn = "";
   public int FloorNumberToReturn = 0;
   public int RoomNumberToReturn = 0;
   public string StatusToReturn = "";

   public ReservationInformation ReserveRoom(int capacity, string familyName)
   {
      return new ReservationInformation(ReservedForToReturn, FloorNumberToReturn, RoomNumberToReturn, StatusToReturn);
   }
}

This class will sit in the tests project and will be used for testing only. Notice that we can manipulate the data we want to return from this repository. Remember: we want to test the interaction between the classes. We assume that the repository in production was tested and will be able to "know" if the hotel is really full or not. We make a fake one so we can control the internal behavior. We are playing doctors: Assuming that the repository is OK, will our class still behave as we expect ?

We can play with the repository and look at the results we get. Will do the same with the NameResolver later on.


Run the tests - 3 passed, 0 failed. So far, requirements 1 and 2 are set.


What is Stub then ?

stub is an object that helps you test another object. You never ever Assert the stub itself.
You use it only as an helper while you're looking to test another object.

In our example, the FakeHotelRepository is a classic stub. We use it to assert another object - ReservationInformation.


Let's take a pause here as this post cover a lot of material and code. The next post will answer the third requirement and I'll present additional solution for the problems we encounter here.

TDD
Posted by Oren Ellenbogen 
21/07/2006 12:57, Israel time UTC+03:00,     Comments [4]  |