views:

123

answers:

4

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.

+6  A: 

For unit testing to be feasible, your code will have to apply to principles of cohesion and decoupling. In fact, it will force those principles on your code as you apply it. Meaning, if your code is not well factored (i.e. OO design principles applied correctly), unit testing will be next to impossible and/or useless.

So probably, the better way for you to think about this would be 'How can I divide up all the work of my application to smaller, more cohesive pieces of code that only do one or two things and use those to assemble my application?'

Until you have internalized this mindset in terms of how you think about your code, unit testing will probably not make sense.

Mahol25
It's actually a bit of a chicken and egg situation. You cannot write good unit tests without well designed (testable) code. You cannot design code well (testable) without having taken a stab at unit testing.
Ates Goral
+1, especially for 'force those principles on your code'. In fact, I once read an article that suggested that writing code so that it is easily unit testable actually reduced bug rates more than actually implementing the tests! The act of factoring your code into easily testable units makes correctness more obvious and reduces the chance for bugs. Thinking how you're going to test some code causes you to write it in a more modular form. I've certainly noticed that in my code. Suddenly mocking and dependency injection all fall into place.
Alex Humphrey
+2  A: 

Well, if x + 3 == 8 isn't enough of a hint, what about x == y?

Said differently, what you're testing for is the correct and incorrect behaviour of types or functions, not just when used with regular conditions, but also under unexpected conditions. With a class for example you need to recognize that just instantiating isn't enough. Are the prerequisites of the class met? What about post-conditions? What should happen when these aren't met? This is where you set the boundaries between you and the person using the class (could also be you, of course) to differentiate between a bug in the class or a bug in the usage of the class by the user. Do instances of your class change state when used with particular coding patterns? If so, how so? If not, why not, and (ideally) under all possible usage conditions; is this behaviour correct?
Unit tests are also a good place for a user of a (for example) class to see how the class is expected to be used, how to avoid using it, and what could happen under exceptional circumstances (where if something goes wrong, your class is supposed to react in some particular way instead of simply breaking). Sort of like built-in documentation for the developer.

Geoff
+1  A: 

Perhaps learning from an example would be most useful for you. You could take a look at the NerdDinner sample app and see what kind of testing it does. You could also look at the MVC source code itself and see how it is tested.

marcind
+2  A: 

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!

John Deters