Testing software is critically important to ensuring quality. Automated tests provide a lower Mean Time to Feedback (MTTF) for errors as well as enable developer’s to make changes without fear of breaking things. The earlier in the SDLC that errors can be detected and corrected, the better. (See the Test Pyramid). As engineers on the platform we should practice TDD in order to generate a thorough bed of unit tests. Unit tests alone do not ensure that everything works as expected so we will need gradually more sophisticated forms of testing.
There are different approaches to testing software. This document chooses to articulate types of automated testing by the point in the SDLC at which it is executed and by what it covers. There may be different strategies for testing at each of these lifecycle points (e.g., deterministic, fuzz, property-based, load, perf, etc..)
|SDLC Stage||Type||Target||Who Runs Them?||Description|
|Design / Build Time||Unit||Single Application||Engineer, CI||In process, no external resources. Mock at the Architectural boundaries but otherwise avoid mocks where possible.|
|Integration||Single Application||Engineer, CI||These tests will mostly target the adapters for external systems (e.g., file io, databases, 3rd party API’s, 1st party API’s that are not the component under test.) |
Integration tests differ from acceptance tests in that they should never fail to an issue with an external service.
|Post Deployment to Test Environment||Acceptance||Entire System or Platform||CI, CD||Largely black box, end-to-end testing.|
For bonus points, tie failures into telemetry to see if your monitors are alerting you.
|Manual UX Testing||Entire System or Platform||Engineer, QA, Users||This testing is qualitative and pertains to the “feel” of the platform with respect to the user experience.|
|Post Production Release||Smoke||Entire System or Platform||Engineer, CD||A small suite of manual tests to validate production configuration.|
|Synthetic Transactions||Entire System or Platform||System||Black box, end-to-end use-case testing, automated, safe for production. These tests are less about correctness and more about proving the service is running.|
|Other?||This is not an exhaustive list.|
Emphasize Unit Tests
In general, our heaviest investment in testing should be done at the time the code is written. This means that unit tests should far outweigh other testing efforts. Why?
Unit tests are very low-cost to write and have very low Mean Time to Feedback (MTTF). This means they have the greatest ROI of any other kind of test.
The other kinds of testing are important but they get more complex as you move through the SDLC. This makes covering finicky edge-cases challenging from both an implementation and maintenance perspective. Unit Tests don’t have these drawbacks provided you follow good TDD guidance.
TDD is the strongly preferred manner of writing unit tests as it ensures that all code written is necessary (required by a test) and correct. Engineers who are not used to writing code in a TDD style often struggle with the practice in the early stages. If this describes your experience, be satisfied with writing tests for the code you’ve written in the same commit until it starts to feel natural.
The activity of TDD consists of three steps:
- (RED) Write a failng unit test.
- (GREEN) Write enough productino code to make it pass.
- (REFACTOR) Now make the code pretty.
The unit tests you write should strive to obey the three laws of TDD:
- Don’t write any production code unless it is to make a failing unit test pass.
- Don’t write any more of a unit test than is sufficient to fail; and compilation failures are failures.
- Don’t write any more production code than is sufficient to pass the one failing unit test.
Good unit tests have the following attributes:
- The test must fail reliably for the reason intended.
- The test must never fail for any other reason.
- There must be no other test that fails for this reason.
It’s impossible to fully convey the scope of what you should know about test automation in this document. Below are some resources you may be interested in as you move through your career.
- Test Driven Development: By Example by Kent Beck
- The Art of Unit Testing: 2nd Edition by Roy Osherove
- Working Effectively With Legacy Code by Michael Feathers
- Refactoring: Improving the Design of Existing Code (2nd Edition) by Martin Fowler