Prerequisites
- You have an IDE or a text editor (e.g.: IntelliJ IDEA)
- You have Maven installed
We will be using a few tools/frameworks to facilitate our job.
- JUnit - Unit Testing Framework
- mockito - Mocking Framework for Unit Tests
- junit-dataprovider - Data Provider Runner for JUnit
- Cucumber-JVM - A BDD testing framework implementation for Java
In the folder ./src/test/java/workshop/calculator/README.md you will find the acceptance criteria for the calculator functionality. Try to implement these criteria by the TDD approach en BDD approach. The outcome should at least have 15 tests passing.
Tests serve 3 purposes:
- Acceptance Criteria - Ensures developed code meets specification
- Regression Testing - Ensures new changes don't impact previous developed code
- Documentation - Demonstrates how the application behaves
To achieve proper documentation, a good starting point is to create naming conventions for the tests.
You can define your own conventions keeping in mind that the test methods should clearly identify:
- What is being tested
- What is the Scenario (Input)
- What should be the outcome (Output)
Example with a traditional approach (simple JUnit):
@Test
public void testSum_BothNumbersArePositive_ShouldReturnPositiveNumber() {
...
}
The method name should be read as :
Test sum IF both numbers are positive THEN should return positive number.
Example with a BDD approach:
Feature: Calculator
The calculator supports the sum operation.
Scenario: Adding positive numbers
Given I use a calculator
When I add positive numbers
Then The result should be positive
Tests typically follow the AAA pattern:
- Arrange - Setup of the Scenario, e.g.: creating the input data
- Act - Executing the action/method
- Assert - Validation of the outcome
Example:
@Test
public void testSum_BothNumbersArePositive_ShouldReturnPositiveNumber() {
// Arrange
int a = 10;
int b = 20;
Calculator calc = new Calculator();
// Act
int result = calc.sum(a, b);
// Assert
Assert.assertTrue(result > 0);
}
Mocks and Stubs are used to facilitate testing by solving the problem of dependencies.
When the code you are implementing has a dependency, using this technique, you create a fake object that emulates that dependency. If you are required to define specific return values to emulate a certain scenario then you'll need to use a stub otherwise you'll simply use a mock.
Example:
- Mock
provider = mock(PaymentProviderInterface.class);
broker = new PaymentBroker(provider);
- Stub
provider = mock(PaymentProviderInterface.class);
broker = new PaymentBroker(provider);
when(provider.isAvailable()).thenReturn(false);