A software test is a piece of software, which executes another piece of software asserting that it behaves in a certain way.
With software tests you ensure that certain parts of our software work as expected. These tests are typically executed automatically via the build system and therefore help the developer to avoid breaking existing code during development activities.
Running tests automatically helps to identify software regressions introduced by changes in the source code. Having a high test coverage of your code allows you to continue developing features without having to perform lots of manual tests.
You should write software tests for the critical and complex parts of your application. If you introduce new features a solid test suite also protects you against regression in existing code.
Some developers believe every statement in your code should be tested but in general it is safe to ignore trivial code. For example, it is typical useless to write tests for getter and setter methods which simply assign values to fields. Writing tests for these statements is time consuming and pointless, as you would be testing the Java virtual machine. The JVM itself already has test cases for this. If you are developing end user applications you are safe to assume that a field assignment works in Java.
If you start developing tests for an existing code base without any tests, it is good practice to start writing tests for code in which most of the errors happened in the past. This way you can focus on the critical parts of your application.
The code which is tested is typically called the code under test. If you are testing an application, this is called the application under test.
A test fixture is a fixed state of a set of objects which are used as a baseline for running tests. Another way to describe this is a test precondition.
For example, a test fixture might be a a fixed string, which is used as input for a method. The test would validate if the method behaves correctly with this input.
In testing you distinguish between unit, integration tests and performance tests:
A unit test is a piece of code written by a developer that executes a specific functionality in the code to be tested and asserts a certain behavior or state. The percentage of code which is tested by unit tests is typically called test coverage. A unit test targets a small unit of code, e.g., a method or a class. External dependencies should be removed from unit tests, e.g., by replacing the dependency with a test implementation or a (mock) object created by a test framework. Unit tests are not suitable for testing complex user interfaces or component interaction. For this, you should develop integration tests.
An integration test aims to test the behavior of a component or the integration between a set of components. The term functional test is sometimes used as synonym for integration test. Integration tests check that the whole system works as intended, therefore they are reducing the need for intensive manual tests. These kinds of tests allow you to translate your user stories into a test suite. The test would resemble an expected user interaction with the application.
Performance tests are used to benchmark software components repeatedly. Their purpose is to ensure that the code under test runs fast enough even if it’s under high load.
A test is a behavior test (also called interaction test) if it checks if certain methods were called with the correct input parameters. A behavior test does not validate the result of a method call.
State testing is about validating the result. Behavior testing is about testing the behavior of the application under test.
If you are testing algorithms or system functionality, in most cases you may want to test state and not interactions. A typical test setup uses mocks or stubs of related classes to abstract the interactions with these other classes away Afterwards you test the state or the behavior depending on your need.
There are several potential naming conventions for software tests. A widely-used solution for classes is to use the Test or Tests suffix at the end of test classes names. This convention is also used by popular build system to identify the test to run.
As a general rule, a method name for a name should explain what the test does. If that is done correctly, reading the actual implementation can be avoided.
One possible convention is to use the "should" in the test method name.
This gives a hint what should happen if the test method is executed.
Another approach is to use
given[ExplainYourInput]When[WhatIsDone]Then[ExpectedResult] for the display name of the test method.
With the JUnit5 testing framework this convention is less important, as you can use the
@DisplayName annotation to add a description for the test method.
In your unit tests, you want to test certain functionality (the class under test) in isolation. Other functionality required to test the class under test, should be controlled to avoid side-effects.
A mock object is a dummy implementation for an interface or a class. It allows to define the output of certain method calls. They typically record the interaction with the system and tests can validate that.
You can create mock objects manually (via code) or use a mock framework to simulate these classes. Mock frameworks allow you to create mock objects at runtime and define their behavior.
The classical example for a mock object is a data provider. In production an implementation to connect to the real data source is used. But for testing a mock object simulates the data source and ensures that the test conditions are always the same.
These mock objects can be provided to the class which is tested. Therefore, the class to be tested should avoid any hard dependency on external data.
Mocking or mock frameworks allows testing the expected interaction with the mock object. You can, for example, validate that only certain methods have been called on the mock object.