It seems like every unit test example I’ve encountered is incredibly obvious and canned. Things like assert that x + 3 == 8, and whatnot. I just have a hard time seeing how I’d unit test real world things, like SQL queries, or if a regEx used for form validation is actually working properly.
Case in point: I’m working on two ASP.NET MVC 2 sites that are DB driven. I have a test unit solution for each of them, but have no idea what kind of tests would be useful. Most of the work the site will do is writing data to, or retrieving and organizing data from the DB. Would I simply test that the various queries successfully accessed the DB? How would I test for correctness (e.g., data being written to the proper fields, or correct data being retrieved)?
I’m just having a hard time transforming my own informal manner of testing and debugging into the more formalized, assert(x) kind of testing.
First, ask yourself “Why are unit tests hard to write for my real code?” Perhaps the answer is that your real code is doing too much. If you have a single module of code filled with “new” statements and “if” statements and “switch” statements and clever math statements and database access, it’s going to be painful to write one test, let alone adequately test the logic and the math. But if you pulled the “new” statements out into a factory method, you could get easily provide mock objects to test with. If you pulled the “if” clauses and “switch” statements out into state machine patterns, you wouldn’t have so many combinations to test. If you remove the database access to an external data provider object, you can provide simple test data to execute your math statements. Now you’re testing object creation, state transitions, and data access all separately from your clever math statements. All of these steps got easier by simplifying them.
A key reason code is hard to test is that it contains “internal dependencies”, such as dependencies that it creates, or dependencies on libraries. If your code says “Foo theFoo = new Foo();” you can’t easily substitute a MockFoo to test with. But if your constructor or method asks for theFoo to be passed in instead of constructing itself, your test harness can easily pass in a MockFoo.
When you write code, ask yourself “how can I write a unit test for this code?” If the answer is “it’s hard”, you might consider altering your code to make it easier to test. What this does is it makes your unit test the first actual consumer of your code – you’re testing the interface to your code by writing the test.
By altering your interfaces to make them easier to test, you will find yourself better adhering to the object oriented principles of “tight cohesion” and “loose coupling”.
Unit testing isn’t just about the tests. Writing unit tests actually improves your designs. Get a little further down the path, and you end up with Test Driven Development.
Good luck!