Registered: Sep, 2005
Re: A Set of Unit Testing Rules
Posted: Sep 14, 2005 12:47 AM
> > <i><p>A test is not a unit test if:</p>
> > <li>It talks to the database
> > <li>It communicates across the network
> > <li>It touches the file system
> > <li>It can't run at the same time as any of your other
> > unit tests
> > <li>You have to do special things to your environment
> > (such as editing
> > config files) to run it.
> Sometimes, your unit test must do some of these things:
> 1. You test a class who's function is to interact with a
> db, write/read a file, or write/read the Windows
You don't need to write a unit test that accesses the database for every piece of code that accessed the database. Here's what you do: encapsulate all access to your database through an implementation of an interface called MyDatabaseService (or whatever). This class itself can either be:
- tested using the unit test tool of your choice
- or simply tested by inspection (it's a really thin layer, and hey, you are pretty sure that Oracle tested their JDBC adapters eh?!)
From then on, all code can be tested against a mock version of the MyDataBaseService. You can us inversion of control at run time (see Spring) to enable this sort of thing easily.
What you should ask yourself is this: what am I testing? Am I testing MY piece of code, or am I testing the database (or some other external dependency)?
Where I work, we use this mocking technique to return fake values etc, and it is very effective. The tests are testing OUR code, and we have 2000 JUnit tests that run in about 35 seconds. Only about 10 tests go out to the network. We have very very very few bugs in our system, so it definitely works.
> 2. You develop a client; to make sure it works, it
> must interact with a server. At my work, for
> example, our client must exchange timestamped, signed, XML
> messages with a server. The test will not be complete
> without server interaction. Actually, you simply
> cannot test this without a server.
Same issues here, any external dependency can be hidden behind an interface that can be mocked out at test time. The mock will have no logic: it will simply return the data required for the code under test to work. At run time , you replace the mock with the real implementation that actually goes to the server.
When mocks are done well, they have zero logic. I cannot stress that enough: usually the body of a method in the mock is empty, or simply returns a default value.
Of course you will still have intergration tests, and they will use the REAL implementations of your 'external system' interfaces. You can use JUnit for these if you like, but strictly speaking, they are not unit tests. Where I work, none of our integration tests have ever failed, not because we are the best developers the world has ever seen, but because the 'external service' implementations are so thin. And I don't want to test that JDBC works, TCP/IP works, Swing works, etc : I want to test my code.
Unit tests should also be self contained, have no static state and should be able to be run one at a time, in any order you please (TestNG relaxes this a bit).
- ) self contained; they are testing my code, and are defined entirely in code. I don't want my tests to fail because someone deleted some records from the database.
- ) no static state; for the guarantee that there are no 'magic' dependencies between tests. Don't want to get into a situation where one test will only run if test X is run first. This means static variables in my code are readonly, and static initializers are avoided if possible.
- ) no external dependencies; if tests fail, it is always because I screwed up the code and for no other reason
> You want to say these are not unit tests? I'll argue that
> the tests can't be any simpler.
Hopefully I have illustrated that this is not the case.