Some time ago, I have discovered, that using your own custom test double classes, instead of a test framework makes my test code more readable and maintainable. Here is an example (pseudo-code):
1
2
3
4
5
6
7
8
function test_password_revealer_when_toggled_reveals_password () {
passwordController = MockPasswordController . new ()
passwordRevealer = PasswordRevealer . new ( passwordController )
passwordRevealer . toggle ()
expect ( passwordController . isRevealed ()). toBeTrue ()
}
The same test with mocking framework would look this way:
1
2
3
4
5
6
7
8
9
10
function test_password_revealer_when_toggled_reveals_password () {
passwordController = MockFramework . Mock . new ( "PasswordController" )
passwordRevealer = PasswordRevealer . new ( passwordController )
spy = spyOn ( passwordController . reveal ())
passwordRevealer . toggle ()
expect ( spy . haveBeenCalled ()). toBeTrue ()
}
If you take a closer look at the last example, and, specifically at these 2 lines:
1
2
3
spy = spyOn ( passwordController . reveal ())
expect ( spy . haveBeenCalled ()). toBeTrue ()
They use a language, that is not relevant to the domain, therefore, they make the test less readable.
Additionally, they have knowledge of which exactly method PasswordRevealer#toggle()
should call on passwordController
. If we were to rename reveal
method, all tests for PasswordRevealer
would fail.
The same thing would happen if we were to extract methods/functions/objects out of the PasswordRevealer
.
Of course, creating such test doubles yourself will involve some boilerplate code - this is a trade-off. Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MockPasswordController () {
this . state = "hidden"
this . reveal () {
this . state = "revealed"
}
this . hide () {
this . state = "hidden"
}
this . isRevealed () {
return this . state == "revealed"
}
}
Making this trade-off, will simplify the case when we were to change the name: we would change the name at 3 places:
in test double class,
in caller class,
in “real” implementation class.
Whereas with a mocking framework, we would have to hunt for all failing tests, and usually, it means hundreds of failing tests.
Thank you!
If you, my dear reader, have any thoughts, questions or arguments about the topic, feel free to reach out to me on twitter: @tdd_fellow .
If you liked my ideas, follow me on twitter, and, even better, provide me with your honest feedback, so that I can improve.
Alex has been developing software for the past 20+ years, out of which, 12+ commercially. He has worked for a diverse variety of businesses: early-stage and late-stage start-ups, established product companies, agencies, and consultancies; and in a variety of industries: education, accounting, housing, online marketplaces, automotive, fintech, blockchain, privacy, online marketing, productivity, cloud, and design. He has given tech and inspirational talks at international conferences and local meet-ups since 2015. Alex has spent a significant chunk of his time coaching software developers, engineering managers, and start-up CTOs. He has written production code in 20 programming languages on the back-end, front-end, native mobile, and desktop. Alex cares the most about software industry, professionalism, quality, and well-being of developers.
Jun 21 st , 2016 8:04 am
architecture , clean-code , mocking , pseudo-code , testing
« TDD #5: English Numbers Kata
TL;DR on Sustainable Software Development Paper »