Hadi Hariri's Blog

Additional code just for testing

Sunday, 8 February 2009 13:07 by Hadi

Take a look at this code:

 

   1: public class ClassToTest {
   2:  
   3:     public void MethodToTest()
   4:     {
   5:         
   6:         IDAL dal = new DAL();
   7:  
   8:         int discount = dal.GetDiscount();
   9:  
  10:         if (discount > 10)
  11:         {
  12:             // Do something
  13:         }
  14:  
  15:     }
  16: }

The tight coupling to DAL just stands out like a sore thumb. One consequence of this tight coupling is that it's hard to test if we don't have an underlying database setup with correct values. Forget about testing this while taking a shower, unless you have a habit of taking your SQL Server box to the bath tub with you.

Take a look at the following approach to making this code testable without having a database:

   1: public class ClassToTest {
   2:  
   3:     public void MethodToTest()
   4:     {
   5:         
   6:         DAL dal = new DAL();
   7:         
   8:         #if testing_in_shower
   9:         int discount = 3;
  10:         #else 
  11:         int discount = dal.GetDiscount();
  12:         #endif
  13:         if (discount > 10)
  14:         {
  15:             // Do something
  16:         }
  17:  
  18:     }
  19: }

 

Are you still here? Didn't you just fall off of your chair, not knowing whether to laugh or cry in anguish? It's just plain wrong isn't it? Code Smell just takes on a whole new dimension (and consistency?). Something like this shouldn't be done for many reasons. It makes our code testable but it's wrong.

 

Introducing code for testing purposes

The reason I bring this up now is because in the past 2 weeks I've seen a few references and examples of introducing code just for the sake of testability. Two weeks ago I was skimming over an article that was explaining unit tests. The class the author was trying to test had a dependency that was causing issues during his tests. The solution offered was to introduce a new constructor and inject the dependency in. In our case, the ClassToTest would like:

 

   1: public class ClassToTest {
   2:  
   3:     public ClassToTest()
   4:     {
   5:         
   6:         // Initialize some stuff
   7:  
   8:     }
   9:  
  10:     public ClassToTest(IDAL dal)
  11:     {
  12:         // Initialize some stuff
  13:         _dal = dal;
  14:     }
  15:  
  16: ....

Now that's great. The problem is that the author left both constructors in there, emphasizing that one is for testing purposes and the other for production code. Why that's wrong I'll get to in a moment.

The other day, Scott Hansleman made a blog post on testing some code that had a dependency on the current principal. His solution was to inject IPrincipal into various methods of the Controller, where Controller is in the context of an ASP.NET MVC Controller. Personally I don't agree with the solution but that's another subject (and another post on a different approach I have in the backlog for the same scenario). However, injecting the dependency seemed valid enough. Later on he updated the post with the following:

 

UPDATE: Phil had an interesting idea. He said, why not make method overloads, one for testing and one for without. I can see how this might be controversial, but it's very pragmatic.

  1. [Authorize] 
  2. public ActionResult Edit(int id) 
  3. return Edit(id, User); //This one uses HttpContext
	[Authorize]
	public ActionResult Edit(int id)
	{
	return Edit(id, User); //This one uses HttpContext
	}
	

You'd use this one as before at runtime, and call the overload that takes the IPrincipal explicitly for testing.

The same thing has happened as in the case of an overloaded constructor. Code has been introduced into the base just for the sake of testability. In the previous case it was a new constructor, and in this case, a new method.

 

Why is this wrong?

Introducing code just so that you can test it is wrong for several reasons, including an increase in noise, decrease in readability and more importantly, permitting the possibility of production code that will not be tested. It might seem improbable at first, seeing that the overloaded versions always call the base ones, but as your code base grows, by allowing this habit, you can promote it to other structures such as conditionals, switch statements, etc, and not limit it only to methods or constructors. Then you'll start initializing values that only make sense in tests and gradually your code starts to smell more and more. To top it off, you can throw in some compiler conditional that rips those parts of the code out from the production version.

Code should also serve as documentation. The cleaner you make your code, the less noise and redundancy you add, the easier it is for others to understand and maintain.

 

Conclusion

Being pragmatic is good, but don't do it at any cost. Try and not introduce code into your code base that is there exclusively for the purpose of testability (and no, dependency injection is not only for testability). Anything that has to do with testing, keep it where it should be, in the test assemblies. Having overloaded methods or constructors, mocks or stubs in your code base is just as bad as having compiler conditionals in your code for testability. If you run into issue where your code is hard to test, step back and re-think your design. Maybe the problem you're facing now that's impeding your tests is actually a can of worms waiting to be opened. And the sooner you open it, the better. That's one of the great advantages of Test Driven Development.

Tags:   , ,
Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Is it time to rename TDD?

Saturday, 7 February 2009 19:07 by hadi

Just reading a comment on Uncle Bob's blog by a guy called Ted.

What’s with this whole TDD/SOLID and Podcast stuff?

Just because you practiced TDD does not make your code clean nor high-quality.

Let’s face it, most TDD practitioners are “DEVELOPERS”. You are not “QA”, “TESTER”, “SDET”. What do you know about various testing techniques, testing principles, testing metrics, how to test, etc?

    Do our field a favor TDD-er, do the following:
  1. How We Test Software at Microsoft
  2. Go to MS and apply for SDET position (or Google)
  3. Read more books about Software Testing
  4. Ask yourself if you wrote great TDD code or not

Now for the last question, if the answer is “Yes”, then you’re a Tester. If the answer is “No”, then you’re a developer. And obviously, if you are a developer, you suck at TDD.

It’s just how the brain is wired or perhaps it’s in your training. Developers are almost always bad testers, vice versa.

Maybe TDD should be renamed "Coming up with a good design by first trying to use my code Driven Development". There is an overwhelming amount of information regarding Test Driven Development and yet, still, STILL, people think it's about testing.

Tags:   , ,
Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (6) | Comment RSSRSS comment feed

Naming tests, once again

Saturday, 20 September 2008 07:32 by Hadi

I'm coming across a lot of tests with names like this:

AddEmployee_Should_Pass_When_Not_Duplicate() {.....}

AddEmployee_Should_Fail_When_Duplicate() {....}

For the purpose of this post, I don't care about anything other than what's in bold and red.

The word Pass in there is completely irrelevant and useless. The word Fail is just mind-blowing. What should fail? The test? What if the test passes? Does that mean it has really failed? Or when it fails it means it passes?

Now we all know what Fail means here. It means the test will pass but the call will fail. But it doesn't indicate to us what failure consists of and therefore it's not adding information. Someone looking at the test cannot validate it.

When naming, indicate what the test is trying to validate, not what the outcome is; the outcome is already known: pass or failure.

Something along the lines of Add_Employee_Should_Throw_Exception_When_Employee_Already_Exists would add substantial information for the person reading your test to validate the functionality. But more importantly, when the test fails, they would know what has failed and have valuable information for debugging. 

Tags:   , , ,
Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

User Interfaces and TDD

Sunday, 14 September 2008 19:27 by Hadi

I was having a conversation with a colleague of mine the other day. We were talking about user interfaces and how a sketch of what the interface should look like would help him have a clearer picture of the design. This is not that odd. Many use the same technique to show customers what a possible interface of the end application could look like. The customer can then evaluate whether he/she likes it or prefers to change some detail (and it's never just a color). 

For this purpose, some use designer tools to sketch out a user interface, while others work with development tools hoping that they don't have to throw out the prototype they printed on paper to show the customer.

image

A few decide to make hand-made sketches because they believe showing the customer a WinForm/WPF/PNG can mislead them into believing that the work is nearly finished. If that is the case, educate the customer, don't blame the "messenger" (be it whatever electronic format).

image

 

Irrelevant of the technique used, many see this as a natural process; it's not peculiar or odd to show a customer what the user interface might look like, despite the fact that behind that user interface there is no functionality.

 

 

Take the previous search screen. It was displayed to Joe (a fictitious customer) and he decided he didn't  like it . He said he preferred to have to click on a Search Records button which then would pop up another screen where he would fill in the data and then hit Search (Joe was having a bad day).

image

 

The developer took the feedback into account and designed this particular functionality (not what Joe wanted but what the developer thought Joe really needed). Is there anything wrong with that approach? No. It is perfectly valid. Customer feedback is a good thing, especially when it's really taken into account.

 

Now let's change roles. Let's make the customer be the developer and the user interface be a specific class. What is the developers's user interface to that class? It is the API of the class. It's the properties and method calls. The requirement of class, much in line with that of the form, is to search for particular customers based on a series of fields and return a list of them.

The developer starts out by designing the API like so:

   1: public class CustomerSearch
   2:     {
   3:         public ICollection<Customer> Search(string name, string surname) {
   4:             
   5:             // Call database
   6:             // Add search conditions
   7:             // Filter records
   8:             // return records
   9:  
  10:             return new Collection<Customer>();
  11:         }
  12:         
  13:  
  14:     }

[At this point, you must be wondering WTF is this all about? To be honest, I've lost track myself, but let's hold the thought for a moment...] 

The developer, Jack, then starts to use the interface and as he's a good developer, his first real usage of the interface is a test. As he's writing the test, he realizes that he's made a mistake. He's limited the Search method to two fields, and the customer record has five fields. He then goes back to the class and changes the method to use specifications.

   1: public class CustomerSearch
   2: {
   3:         
   4:     public ICollection<Customer> Search(Criteria criteria) {
   5:     
   6:         // Call database
   7:         // Add search conditions
   8:         // Filter records
   9:         // return records
  10:  
  11:         return new Collection<Customer>();
  12:     }
  13:     
  14:  
  15: }

He then changes the whole code to implement the new functionality. Once he's happy with the result, he then returns to the test and starts to write the test, happy that he's accomplished his objective. Problem is, he threw away 2 hours of work by not thoroughly thinking through the requirements.

But you must be thinking why he hadn't thought of this beforehand. I mean it is pretty obvious, specially if you've done this before. The reason is that you don't know it all up front. It's only when you start using something that it "hits" you. Just like customers don't know everything up front, and they ask for changes as they start to use the program, as developers we don't know it all up front either. As much design as we try to do on paper, it's not until we actually start to use our code that we find we need to change our design.

 

 

Jack could have skipped the first implementation altogether. He could have started using the interface before implementing it completely. Wait a second. How could he use the class before it existed? Well let's think back. What was Jack's usage? He started writing his test like so...

   1: [Test]
   2: public void CustomerSearch_When_Name_Is_Joe_Should_Return_All_Customers_Named_Joe()
   3: {
   4:     CustomerSearch customerSearch = new CustomerSearch();
   5:     
   6:     customerSearch.Search(....    
   7: }

It's when he started writing Search when he realized that he only had two fields and that a customer could have up to five fields. This is where he realized he needed more fields, and the initial design of passing in field names was not very scalable.

 

Welcome to Test Driven Development

If Jack had started to use his own code before implementing it, he would have realized his shortcomings. Here is where TDD comes into play. TDD is not about writing your unit test first, it's about designing your code. It's about you, as the user, defining your user interface. Much in the same way we sketch out a user interface for the customer to give his feedback before we implement the functionality, writing tests is defining the calls you need for your classes before implementing them. As an added value, you end up with a test so that as you re-factor your design to suit your needs, you can validate the functionality.

 

I know this is old news to many, but still to date, it amazes me at the amount of developers that still think that Test Driven Development is about writing tests first. It is, but not to have your test, but to define your usage.

 

 

 

Tags:   ,
Categories:  
Actions:   E-mail | del.icio.us | Permalink | Comments (6) | Comment RSSRSS comment feed