Testing and design quality
Введено| English | Значение |
|---|---|
| refactoring/rɪˈfæktərɪŋ/ | refactoring |
| unit test/ˈjuːnɪt test/ | unit test |
| arrange | arrange |
| act/ækt/ | act |
| assert | assert |
| single responsibility principle | single responsibility principle |
| coupling | coupling |
| cohesion/kəʊˈhiːʒn/ | cohesion |
Did the change break anything?
- You move the fine calculation out of
Loaninto its own class. The program still starts, but is every result still the same? - Clicking around proves very little. A good test suite answers in one second, and this lesson shows how to write one.
Tests pin behaviour
- Refactoring 重构 changes structure without changing behaviour. It is only safe when the behaviour is pinned down.
- Unit tests 单元测试 are those pins: each one checks one promise the code makes.
Arrange, act, assert
- One test, one behaviour: arrange 准备 the objects, act 执行 with one call, assert 断言 the outcome the caller can see.
def test_renew_ok():
loan = Loan(20) # arrange
out = renew(loan, 7) # act
assert out == 27 # assert
Put the parts of a unit test in order.
Сложность 3/5 · 200 монет + небольшой бонус за первое завершение сегодня
Arrange, act, assert — one behaviour per test.
Behaviours, not methods
- Name tests after behaviours: "renew past the limit raises" is a behaviour;
test_daysis just a method name. - A behaviour test survives renames and fails exactly when a promise changes. Assert what callers see, not private fields.
A good unit test is named after —
Сложность 1/5 · 100 монет + небольшой бонус за первое завершение сегодня
Behaviour names survive renames and fail exactly when a promised outcome changes.
A test that asserts the private field loan._days is a good behaviour test.
Сложность 1/5 · 100 монет + небольшой бонус за первое завершение сегодня
It pins storage, not a promise, so it breaks on refactors. Assert what the caller sees, such as the returned days.
Test the boundary
renewallows up to 30 days, because the check is> 30. Test three points: inside (20 + 7 = 27), exactly on the limit (20 + 10 = 30, allowed) and past it (20 + 11 raises, loan unchanged).- The boundary test catches the classic slip of writing
>=for>.
The rule is "raise if days + extra > 30". What does renew(Loan(20), 10) return?
Сложность 2/5 · 140 монет + небольшой бонус за первое завершение сегодня
20 + 10 = 30 is not more than 30, so the renewal is allowed.
The coverage grid
- List behaviours down the side and tests across the top. Each filled cell is a behaviour a test pins.
- An empty row is the next test to write. Coverage follows the list of promises, not the lines of code.

An empty row in the coverage grid means —
Сложность 1/5 · 100 монет + небольшой бонус за первое завершение сегодня
Rows are behaviours, columns are tests: an empty row is the next test to write.
Match each test to the behaviour it pins.
Сложность 2/5 · 140 монет + небольшой бонус за первое завершение сегодня
One behaviour per test; the grid's empty row is the next one to write.
Refactor under green
- Move fines into a
FineCalculatorclass (the single responsibility principle 单一职责原则: one reason to change), and the renew tests still pass untouched. - Coupling 耦合 (how much classes depend on each other) went down; cohesion 内聚 (one purpose per class) went up. That is the direction of every good change.
Moving fine calculation into FineCalculator is safe when —
Сложность 1/5 · 100 монет + небольшой бонус за первое завершение сегодня
Green tests through the move are the proof that structure changed and behaviour did not.
Worked example: one bug, one red test
- The three renew tests run and print
3 passed. - Someone changes the check to
>= 30. Nowtest_renew_boundaryfails and the other two pass: one red test names the exact broken promise.
The check is wrongly changed to days + extra >= 30. How many of the three renew tests fail?
Сложность 2/5 · 140 монет + небольшой бонус за первое завершение сегодня
Only the boundary test (20 + 10 = 30) changes outcome. 27 still passes and 31 still raises.
Three common slips
- Asserting private internals. Every refactor then breaks the test.
- Sharing changeable state between tests. Test B passes only if test A ran first.
- Chasing line coverage on getters while the limit branch has no test.
Recap
- Arrange, act, assert: one behaviour per test, named after the behaviour.
- Test inside, on and past every boundary; the grid shows the next test to write.
- Green tests make refactoring safe; aim for low coupling and high cohesion.