"a place for my stuff..." -- a blog by John Nelson

 

 
 

While catching up on some podcasts, I listened to Hanselminutes #169 with Roy Osherove.  Scott and Roy discuss the art of unit testing.  I’m a fan of unit testing, but specifically in writing high quality unit tests.  A few of the points Roy made were so good, I had to jot them down.  This post is my summary of the podcast.

What is a unit?

Understanding what a unit is helps cut down meta-test time.  Define a unit that makes sense to your particular framework.  Some people say to “Test only the smallest piece of code.”  Roy was a little more specific in providing the following list of criteria for unit testable candidates:

  • Must run only in memory
  • Must run fast
  • Does not touch any external state (database, file system, etc.)
  • Is Repeatable
  • May touch multiple classes/methods

The 3 Pillars of Unit Testing

Roy describes the 3 pillars of unit testing with the following questions:

Trustworthiness.  Are you able to run your test and trust the results?  Do you watch your test pass, and then debug it anyway?  If you don’t trust the test, it’s not saving you time.  Mixing integration tests with unit tests reduce trust.

Maintainability.  Maintenance is abandoned because maintaining costs are too high.  Tests that are not run or maintained are no longer useful, which leads to decreased trustworthiness over time.

Readability.  Do the unit tests conform to naming conventions?  Are they organized logically?  Readability is a factor that can deter maintenance and trustworthiness.  If the person working with the test cannot read it easily, they might not understand what the test is for or what the results are meant to signify.

“Remove one of these pillars, and the rest will fail.”

The Blurry Line:  Unit Testing Versus Integration Testing

In lieu of the integration test versus unit test dilemma, I defer to this StackOverflow question.  It really depends on the situation, and what kinds of boundaries are being crossed within the test in question.

Integration tests come in handy for things the require state.  Testing a Data Access Layer should be done with an integration test, as opposed to a unit test.  Use a real database in the tests, and let the DAL be the test for the database.  Integration tests can be used to test several aspects of state dependent functionality.  Given a logging scenario, the integration test would verify that logging occurred, and assert that the result was the expected outcome, and then cleanup.  Unit tests would test the interaction without the actual logging functionality.

Get the Terminology Right

So often in unit testing you need setup methods and tear down methods.  In order to reduce the scope of what the test is actually using, other incorporated functionality of the framework must be used artificially.

  • Fakes look like an object, but is fake for the test.
  • Mocks are fake objects that decide if the test passes.  Assert against mocks to pass or fail tests.
  • Stubs help your test run.  Don’t assert against them, they just mimic the system.

Assertions

One assert is plenty for any unit test.  However, you might have more than one assert for a unit test if an object result needs to have a specific combination of property values.  That still boils down to being one assertion because the combination of assertions dictate a single expected result.

Test Names

Name unit tests with the Name of the Method, Scenario, and Expected Behavior.   Including this kind of information helps reduce the amount of time a programmer would have to spend figuring out what the unit test is supposed to do, and furthermore encourages the test to stay on track; it keeps intentions clear.

Related Links:

The Art of Unit Testing: with Examples in .NET — http://www.amazon.com/Art-Unit-Testing-Examples-NET/dp/1933988274/ref=sr_1_1?ie=UTF8&qid=1249321351&sr=8-1

Monday, August 03, 2009 1:45:24 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] -
Hanselminutes | Integration Testing | Roy Oscherove | Scott Hanselman | Unit Testing

For many people (myself included), Test Driven Development is far from instinctive.  It even takes some inward reflection to learn how you, as a programmer, design and architect while coding.  The very first question I had was “How do I drive development with tests?”  In some ways this is a can of worms.  Deeper and deeper you’ll tumble down the rabbit hole, because TDD is exciting. Let’s start with the step-by-step recipe for TDD:

  1. Write a test that fails.
  2. Write code to make the test pass.
  3. Refactor ruthlessly to simplify.
  4. Rinse, repeat.

That is a breakdown of the mechanical process.  Knowing it is only the first step.  With this in mind, I would like to share my opinions and observations of this design method.

Unit tests can read like low level specifications.  A practical example is that someone jots down some pseudo code and asks you to write a class based on how they would use it.  Fundamentally, TDD is taking that idea and going pro.

Writing tests as simple examples is a great starting point for high quality design.  Focus on showing how simple it is to set up an object.  I have noticed that writing tests first influences me to write shorter methods, as well as simpler classes.  Small chunks are easier to understand, and TDD can definitely help you stay on that track.

Some other side effects of writing the tests up front is that you end up with a suite of tests at the end of the process, resulting code is more extensible , easier to maintain, and it is a way to gain instant gratification.  It is somewhat thrilling (okay, maybe that’s just me…) to see a test compile and run.

All of these things are great, but let us remember that TDD is design.  Tests are a great part of the process, but it’s something we are borrowing as the means to achieving design goals.  By nature, test driven development lends itself to the SOLID principles.

TDD especially lends itself to the “Single Responsibility” principle.  Singleton objects tend to be easier to test, and help create clear dependencies.

”If you start with simplicity, and expectations and demands for simplicity, you’re going to end up with higher quality design.” - Scott Hanselman

The overall process of TDD helps to detect code smell, or any symptom in the source code that indicates something may be wrong.  As a rule of thumb, if you don’t enjoy setting up the test, something can probably be improved.  This is referred to as “Test Friction.”  There should be a threshold of discomfort when setting up complicated tests.

Another TDD warning flag is solubility—how hard is it to understand code?

“How much effort do you have to make before you can learn what an object does?” – Scott Bellware

Code smell is an excellent indicator that code needs to be refactored.  Having the suite of unit tests that you (presumably) wrote before (or at least while) coding will be the safety net for refactoring.  Refactoring is successful if tests pass.

References:

Hanselminutes, Episode #31- Scott Hanselman discusses TDD with Carl Franklin
Hanselminutes, Episode #146 – Scott Hanselman discusses “Test Driven Development is Design” with Scott Bellware

Wednesday, July 08, 2009 10:21:30 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1] -
Code Smell | Hanselminutes | Refactoring | Scott Hanselman | TDD | Testability | Unit Testing

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
John Nelson
Sign In
Statistics
Total Posts: 24
This Year: 2
This Month: 0
This Week: 0
Comments: 1
 
  All Content © 2010, John Nelson