Testing is a very important part of the software creation process. Without automated tests, it’s very easy for bugs to creep into software.
In fact, some go as far as to say that you should write tests before you write the code. This is called TDD (test-driven development).
There are multiple test frameworks and tools to help you test your code. This book will cover some of these tools, JUnit and Spock.
Types of Tests
Unit test: Test conducted on a single API call or some isolated code or component
Integration test: Test of a larger system that integrates together two or more components
Acceptance test: High-level test that matches the business requirements
Compatibility: Making sure that things work together
Functionality: Ensuring that stuff works
Black box: Test conducted without knowing/thinking about what’s going on in the code
White box: Tests written with the inside of code in mind
Gray box: Hybrid of black and white box testing
Regression: Creating a test after finding a bug, to ensure that the bug does not reappear
Smoke: A huge sampling of data use during a test
Load/stress/performance: How the system handles load (a lot of traffic to a web site for example)
The type and number of tests you write vary, based on a number of factors. The simpler a piece of code is, the less testing it requires. For example, a getter or setter does not require a test by itself.
JUnit
JUnit1 is a simple framework to write repeatable tests.
A typical JUnit test consists of multiple methods annotated with the @Test annotation.
Use @BeforeEach to annotate initialization methods that are run before every test and @AfterEach to annotate breakdown methods that are run after every test. The methods should ensure that each test is independent.
The first parameter to the assertEquals method is what is expected, and the second parameter is the actual value to test. When the two values are not equal, this will throw an Exception and the test will fail. A failing test means our expectations were not met by the code. The software should be considered incorrect at this point and not go any further until the test is successful (not failing).
Hamcrest
This test would assert that map’s size is 2. There are many other matchers and you can even build your own.
Assumptions
When an assumption fails, the test is either marked as passing or ignored, depending on the version of JUnit.3
Spock
What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers. Spock is inspired from JUnit, RSpec, jMock, Mockito, Groovy, Scala, Vulcans, and other fascinating life forms.
Spock Basics
Test classes in Spock are called Specifications . The basic structure of a Specification in Spock is a class that extends spock.lang.Specification and has multiple test methods (which may have descriptive String names). The class’s name should end with Spec, for example, a Specification about a Vampire class could be named VampireSpec.
Spock processes the test code and allows you to use a Groovy-based syntax to specify tests. Spock tests should go under the src/test/groovy directory.
Each test is composed of labeled blocks of code with labels like when, then, and where. The best way to learn Spock is with examples.
A Simple Test
As shown, assertions are simply Groovy conditional expressions. Every line after then: will be tested for Groovy truthiness. If the == expression returns false, the test will fail and Spock will give a detailed printout to explain why it failed.
Mocking
Mocking is when you use a tool to extend an interface or class that can mimic the behavior of that class or interface within a test to assist testing your code. In JUnit tests, you need to use a library, like Mockito,5 to mock other classes that are not part of the test.
Expected behavior can be described by using a number or range multiplied by (*) the method call, as shown here (it expects the receive method should be called two times).
The underscore (_) is treated like a wildcard (much like in Scala).
Lists or Tables of Data
Spock allows you to use lists or tables of data to more simply test multiple test cases within one test.
The overloaded << operator is used to provide a list for the event variable. Although it is a list here, anything that is Iterable could be used. This has the effect of running the test for each value in the list.
The range 1.._ here means “one or more” times. You can also use _..3, for example, to mean “three or fewer” times.
In this case, the two columns (name and length) are used to substitute the corresponding variables in the expect block. Any number of columns can be used.
Expecting Exceptions
After writing some tests, run the tests with either Gradle or Maven. For Gradle run ./gradlew test (results go under build/reports/tests/test/). For Maven run mvn test (results are under build/surefire-reports/).
Other Test Frameworks
There are many other test frameworks that unfortunately we do not have time to cover. Some are used to enable automated browser testing of web applications, like Geb7 and Selenium.8
Summary
In this chapter, you’ve learned how testing is a very important part of software development and the types of tests you should write. You’ve learned about how to use JUnit and Spock for running tests and learned that other test frameworks exist for running integration tests or BDD.