Clean tests, Part 3: More about names
In the first part of this series we looked at the names of our tests. In part two we looked into the structure of the code inside the tests. So there is so much more to look at.
Be clear
There is more about naming than just the method name. How should we name instance of the class under test? Chances are high that you just use the same approach as with any other variable. For example something like this:
SomeClass someClass = new SomeClass();
How will the reader find out which class you are testing?
You might argue that you have a 1:1 naming. Every class has a test with the same class name plus the suffix Test
. By the class name it’s easy to know the class under test.
But this won’t help finding at first glance, in all your instance variables, the field that is representing the class. There might be many instances you need for creating your test plus test values and helpers and they are all intermixing.
The rules I suggested had the goal to make very clear what we test. It needs to jump at you, be visible without reading code or even documentation.
That means: lead the eye by the way you write the code! Similar as we did with the method naming. Lead the eye to the class under test.
Let’s look at an example, I took out of one of my own projects.
@Test
public void should_create_log_folder() throws IOException {
File folder = spy(new File(""));
when(folder.mkdirs()).thenReturn(true);
when(context.getCacheDir()).thenReturn(folder);
tested.prepare(logger);
verify(folder).mkdirs();
}
First, note to myself: the first three lines could be extracted into something like:
File folder = createTestFolder()
But it’s easy to spot that this test works with three instance variables:folder
, context
and tested
. It’s pretty self explanatory which one is the one to be tested.
You’ll find this naming convention in thousands of tests that I wrote along the years.
Reduce the noise
Let’s continue with this example, let me show something from this class:
public class FileLoggerPreparationTest {
Logger logger;
FileLoggerPreparation tested;
Context context; @Before
public void setup() {
....
There is something else here, that I repeat all over my test.
If you don’t see it right away, let me write that code the way you probably would write it:
public class FileLoggerPreparationTest {
private Logger logger;
private FileLoggerPreparation tested;
private Context context; @Before
public void setup() {
....
Maybe you would even have private final
when assigned them directly, right?
We’ve been learning since university that encapsulation is important for writing object orientated code. We need to keep all instances private to have a good design.
But here is the point we tend to forget:
Tests have their own rules for good and clean design!
You don’t need to protected those instance variables here!
Why?
Tests run in isolation! Two tests won’t know of each other. This is not an object orientated system as we know it. These are small independent units we write in a object orientated language.
Let’s focus on what is important. Forget about private, forget about final in your tests! Break the rules!
Visibility and clarity win in tests!
Let me repeat: it’s about leading the eye to what is important!
Did you know?
Before we close this chapter a small quiz.
Is there a difference in the behaviours for the following two snippets?
public class FileLoggerPreparationTest {
Logger logger;
FileLoggerPreparation tested; @Before
public void setup() {
logger = new Testlogger();
tested = new FileLoggerPreparation()
}
vs
public class FileLoggerPreparationTest {
Logger logger = new Testlogger();
FileLoggerPreparation tested = new FileLoggerPreparation()
A lot of people would reply that in the first case, the setup()
method will initialise the fields for running each test method, while in the second case, the initialisation happens once.
Do you agree?
This assumption is not true, both behave the exact same way! JUnit creates a new instance for every run of a test method. This means your tests could often be written in shorter way as in the 2nd example.
In the last part of this mini series we will look at reflections, something that is painful for the writer and the eyes….