Testing
Why Test?
Behave the way the user expects.
- detect bug or functionality issues.
- be confident the calculations are correct.
- be confident subsequent changes work as it did before.
Helps define requirements of what you are looking to build.
- helps developers hone in and focus on what is necessary
Others can easily understand.
- convey the intent of the application clearly.
- tests also act as documentation.
Backend can have bugs that you will never find out about until something goes wrong.
- silent type mismatches.
Easy to modify or extend, refactor.
- applications are never static (new feature and functionality) - ensures consistency on change.
- address only what is necessary - getting to agreed test suite.
TDD
Writing test before writing code.
Rapidly define requirements as tests that must be satisfied by the code you later write.
- Makes implementation highly predictable.
// react test import React from "react" import { render } from "@testing-library/react" import App from "./App" test("renders learn react link", () => { const { getByText } = render(<App />) const linkElement = getByText(/learn react/i) expect(linkElement).toBeInTheDocument() })
Run test via npm test
.
Red Green Refactor
# Write a failing test (fail, stop)
# Write a success test (passing)
## functionality may not be complete, but working
null test → pass
stub required objects → test presence → pass
write functionality spec → test failure → create functionality in actual file → test → pass
Test Structure
Test structure should follow:
Setup
Method
Validation/Assertion
Boundaries
Avoid implementation of external dependencies in unit tests.
Using things like dependency injection (and shallow-mocking the dependencies), you can avoid relying on the actual implementations to run properly for the test to pass.
// external dependencies and its implementation tested import ( someExternalFunction ) func testTightCoupling(someParam: string) boolean { testValue := someParam // calling actual implementation (bad) someExternalFunction(testValue); // assertion } // shallow dependencies using mock return value const mockedServiceMethod = mock(someExternalFunction) func testLooseCoupling() boolean { testValue := someParam // calling mocked implementation mockedServiceMethod(testValue) }