Given When Then - Our Testing Approach
Why?
At Brightec code quality is a top priority. Much of our business revolves around selling great code.
Code quality can mean lots of different things.
- It means we adhere to code standards and style guides. These mean that our code appears consistent and uses a simpler or better approach.
- It means all our code is subjected to a code review. This encourages accountability and enables us to collaborate on finding the best solution.
- It means running lint checkers on our code. These help spot inefficiencies and enforce code styles.
It also means we write tests for our code.
Testing
Tests prove that the code you wrote actually does as you intended. This seems counter-intuitive because surely you wrote the code, so it does do what you intended. I tend to agree with this statement but with the caveat that we all make mistakes, all the time. Writing tests helps you to spot the mistakes you made. You get to fix them before they even get committed and shipped.
There a few other side effects to testing which are also beneficial.
- You get the ongoing proof that your code still works, even after making further changes to it.
- It forces you to write testable code. This leads to a better code structure, with small methods and defined dependencies.
- It can also be a playground for trying out new technologies. Since test code isn't production code, introducing new technologies is usually met with less resistance.
How?
Given When Then (GWT) is an approach adopted by many teams. It’s a style of writing tests which lays out a framework for what a test should look like.
Given - This is where you define the state of your application up to this point. You provide any data or context. Usually, this involves setting up mocks or stubbing methods.
When - This is the part of your application you are testing. Usually, this is a method call.
Then - This is where you verify a particular behaviour or outcome. What did you expect to happen?
Simple Example
Here you can see a simple method which multiplies a given number by 2. In the test shown, we have a given of x = 1. We have a when of multiplyBy2(). A then of result = 2.
In summary, given that we have an integer of 1, when we multiply it by 2, then we expect to get 2.
This is a nonsense example, but it demonstrates the GWT structure.
Formatting
Having a clear format to these tests can help any readers of your code understand your approach. It lays out what you think you're given, when and then are. Enabling the reader to challenge those choices.
It also helps you to get into the flow of this pattern.
Test name
The name of your test method appears in your results. So it's important that it gives a clear indication of what you are testing. Therefore, we find it helpful to write out the given, when and then.
We use two underscores to separate the sections of GWT. One underscore can be used to separate words within a section. This can, in complex cases, lead to long test names, so you may have to adopt abbreviations. But long test names are better than ambiguous test names.
Comments
Within the method body, we use comments to separate the sections of GWT. We use capitalized words to offer a distinction between these titles and any other comments we may need.
Spacing
We ensure that there is clear space between the sections of GWT.
Pro tips
Result variable
To enable you to check a result within the then section of your test, you can add a variable to capture it. This allows you to keep clear separation between when and then.
@Before
Many testing frameworks, like JUnit, enable you to add setup code to your test class. This code gets run before the start of every test. Due to the nature of it, this is inherently implied to be part of your given.
Common sense, and applying good practice is needed to decide what goes in the @Before. Since anything within your @Before, may not be immediately obvious to the reader of your test.
We generally only initialize mocks within our @Before. We outline mock behaviours within the test body. We also create the class under test within @Before.
@After
Like @Before, @After, or similar, allows you to add code which runs after every test. This is generally the place you do any cleanup, which if not done, would affect the result of the following test. This sits outside the GWT pattern.
No Given
Sometimes there is nothing to go in your given section. In these cases we still have our given section in the test name and method, we just comment // nothing.
Stubbing
In many tests, you want to verify that a particular method gets called. You don’t need that method to actually run, you just want to check that it would. In this case, you want to stub that method.
Stubs don’t fall within the GWT pattern, as they are simply an efficiency measure. We put them at the top of our test method in a separate section.
Callback behaviour
It can be tricky to handle callbacks within tests. It can be even harder to fit them into GWT pattern.
We would typically describe the flow of doWork() as:
We call doWork(), then when the network request is successful we expect the cache to get updated.
Instead, if you think about it as:
Given that the network is going to be successful what do I then expect this class to do.
This subtle change can help us to fit this kind of test nicely into GWT.
Practically, with JUnit, this can be hard to write. We are tempted to use verify, in combination with an ArgumentCaptor, to reply to the callback. Instead, if you use doAnswer, as shown above, it sits nicely within the given section.
Summary
Using GWT at Brightec has helped us to structure our tests, write single responsibility tests and improve our test coverage. We no longer write bloated tests which are hard to read and understand.
GWT is not the “best” way of writing tests, but it can be a helpful approach to improving your tests.
Looking for something else?
Search over 400 blog posts from our team
Want to hear more?
Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!