Implement unit tests to assert correct behaviour of a function/method
Understand the purpose of Test Driven Development (TDD)
Pre-requisites
Lesson
How do we know that our code is working the way we think it is? Surely we can just run the code and see if it is working. Isn't this just testing the code?
As the programs we write get larger and more complex, it is not always possible to test our code by just running it. For example, we may want to force a particular kind of error to ensure we are handling it correctly.
This is why we write tests. Tests ensure that our software is working as we have designed. We are trying to prevent 3 things:
Errors
Defects (bugs)
Failures
Errors are mistakes made in code.
Defects mean our code works but not as we intended. Defects are often called 'bugs'. This term was coined because before the use of electronic circuits, early computer programs would sometimes malfunction due to actual insects getting into the physical valves of the computer, preventing the program from running correctly.
Failures are when our programme 'crashes' i.e. stops running and errors in a way that cannot be recovered from.
Unit test frameworks
Let's look at how we create a test for the Bag class we created in the previous lesson.
describe('Bag', function () {
test('has a weight', function () {
const bag = new Bag(13);
expect(bag.weight).toBe(13);
});
test('does not have a weight', function () {
expect(() =>new Bag()).toThrowError('bag must have a weight');
});
})
describe('Bag', function () {
test('has a weight', function () {
const bag = new Bag(13);
expect(bag.weight).toBe(13);
});
test('does not have a weight', function () {
// notice how we have to run a function inside `expect` to trigger the error and catch it
expect(() =>new Bag()).toThrowError('bag must have a weight');
});
})
publicclassBagTest{
@TestpublicvoidhasValidWeight()throws Exception {
final Bag bag = new Bag(13);
assertEquals(13, bag.getWeight());
}
@Test(expected = Exception.class)// asserts we have an exception thrownpublicvoidhasInvalidWeight()throws Exception {
final Bag bag = new Bag(0);
}
}
Test coverage
It's really important to test all the possible branches through your code. Your company will have coding standards which specify what % of code coverage is required.
Research how to generate a coverage report for your language's test framework. Here is an example using JavaScript Jest:
Then run npm run test:report - you are aiming for 100% test coverage.
You should see that Jest generates a 'coverage' report in your project folder under /coverage/Icov-report/index.html. Open this in your browser to view coverage by line, branch, function and statement.
The report is interactive hence you can click and drill down into a specific class.
Test Driven Development (TDD)
Test Driven Development requires us to write tests first. The 'red, green, refactor' approach helps developers focus on three phases:
Red — think what the output of the method/function should be, write a test to assert this (it will fail as the method implementation won't yet exist)
Green — add the implementation of the method/function - this should cause your test to pass
Refactor — can you improve your implementation and still make the test pass?
Assignment
Use TDD to write tests for the airport classes you created in the previous lesson
Commit your code into Github and share the link with your coach for review.
Include a README.md file in your Github project that explains the purpose of a unit test.
Assignment extension tasks
Investigate mocking in unit tests - why might mocking be useful for a class which connects to a database?
Research the unit test framework for another language. What similarities can you see with the test framework for your chosen language?