I suspect that we are at a point in the history of our profession where the discipline of writing tests is only just gaining ground and will one day be common place and taught as part of elementary programming classes (1). Today’s blog provides a summary of the of the terms used in my previous blogs in this series.
I covered the definition of a unit test in part two of this series of blogs, giving the views of Shane Warden, who in his book The Art of Agile Development states that unit tests should run at a rate of “hundreds per second”; Michael Feathers, who in his book Working Effectively with Legacy Code states that a unit test is not a unit test if:
- It talks to a database.
- It communicates across a network.
- It touches the file system.
- You have to do special things to your environment (such as editing configuration files) to run it.
I also quoted Roy Osherove, who in his book The Art Of Unit Testing sums up a good unit test as: “an automated piece of code that invokes the method or class being tested and then checks some assumptions about the logical behaviour of that method or class. A unit test is almost always written using a unit testing framework. It can be written easily and runs quickly. It’s fully automated, trustworthy, readable and maintainable”.
Unit tests can be summed up using the FIRST acronym: Fast, Independent, Repeatable, Self Validating and Timely.
When to Use Unit Tests
Unit tests are the bread and butter of testing. If you employ Test Driven Development, TDD, then you write failing tests before you write your production code. If you don’t use TDD, then at least write your tests at the same time as your production code i.e. write a method and then write its tests. This technique doesn’t involve the paradigm shift that accompanies TDD, but it’s much better than writing tests after you’ve written all the code, which is usually considered tedious by developers. There should be one test for every scenario, which translated into plain English means every path through your code: both sides of every if statement and every case of a switch statement. In short, every project should have hundreds of Unit Tests and you should have the confidence that if you change a part of the code then you won’t break something.
Stubs are used to isolate an object under test from the rest of the system. They are objects that are injected into your object to replace real objects in test situations. Martin Fowler defines stubs, in his essay Mocks Aren’t Stubs as:
“Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it ‘sent’, or maybe only how many messages it ‘sent’”
…whilst a similar definition, taken from The Art of Unit Testing is:
“A stub is a controllable replacement for an existing dependency (or collaborator) in the system. By using stub, you can test your code without using the dependency directly.”
Mocks are replacement objects used to imitate, or mock, the behaviour or roles of production objects. This really means checking that an object under test calls methods on a mock object as expected with the test failing if it doesn’t; hence, you’re asserting on the correctness of method calls and the execution path through your code, rather than, in the case of a regular unit test, the return value of the method under test.
Integration tests are the opposite of unit tests. The idea behind integration tests is to prove that your objects collaborate with each other and the system around them. To paraphrase Michael Feathers, integration tests CAN:
- talk to a database.
- communicate across a network.
- touch the file system.
- require you to do special things to your environment (such as editing configuration files) to run it.
Roy Osherove in the Art of Unit Testing states that an “integration test means testing two or more or more dependent software modules together as a group”. For me this constrains the definition a little too much after all, in testing objects in a single module you can access a database or file system whilst figuring out if your objects can collaborate.
In projects I’ve previously worked on, there’s usually been a module specifically written to hold integration tests. This is because integration tests are less numerous than unit tests (perhaps at a ratio of 1:10) and, by virtue of the fact that they access their environment, are usually substantially slower and therefore corralling all integration tests into their own Maven module means that they don’t have to run every time you build a module, speeding up build and development time.
End to End Integration Tests
I’ve covered End to End tests in detail in the second blog in this series, so to summarise, they can be defined as a special case of integration tests in that the test commences at, or just behind, a system boundary and executes through all layers of the system. Where the system boundary, or just behind the system boundary is a matter for debate. In terms of a Spring MVC app, there’s no reason why an end to end test shouldn’t start at your controller code ignoring the browser and dispatcher servlet. After all, I suspect that the Guys at Spring have tested their code thoroughly, so why should you waste time testing it? Also, testing what the front end looks like s a whole different kettle of fish.
(1) I often suspect that testing techniques are not actually taught in collages and universities – but I’ve no evidence of this. If there are any academics out there who can tell me that unit testing is taught, encouraged and an integral part of their Computer Science Degree courses then I’d be happy to hear from them.
Related Articles :
- Testing Techniques – Not Writing Tests
- The Misuse of End To End Tests – Testing Techniques 2
- What Should you Unit Test? – Testing Techniques 3
- Regular Unit Tests and Stubs – Testing Techniques 4
- Unit Testing Using Mocks – Testing Techniques 5
- Creating Stubs for Legacy Code – Testing Techniques 6
- More on Creating Stubs for Legacy Code – Testing Techniques 7
- Why You Should Write Unit Tests – Testing Techniques 8