Writing Good Unit Test
Lucy Lu
Agenda
• Auto-Testing Overview
• Unit Test v.s Integration Test v. E2E Test
• Unit Test Terminology( Fake/Spy/Stub/Mock )
• Sinon (test framework)
• Good Practice for Unit Test
Testing Pyramid
• Proposed by “2014 Google Test
Automation Conference”
• 70/20/10 split
• 70% unit tests
• 20% integration tests
• 10% end-to-end test
Unit Test v.s. E2E Test
• Unit tests are fast
• Unit tests are reliable
• Unit tests isolate failures
• E2E tests simulate real user scenarios
Unit Test
Fake/Spy/Stub/Mock
Fake
• Simplified implementation of dependency
• Coded directly without using framework
• Don‘t provide validation of how the dependency is used
• Used when specific logic is required in dependency
Fake Object
Spy/Stub/Mock in Sinon
Sinon
• Replace dependency with a test-double
• Extensive mocking library
Spy
• A function that records arguments, return value, the value of this and
exception thrown (if any) for all its calls
• Two types of spies
• anonymous functions
• wrap methods that already exist
Creating a spy as an anonymous function
Tested Function
UT
Using a spy to wrap an existing method
Stub
• A Function(like spy) with pre-programmed behavior
• When wrapping an existing function with a stub, the original function is not
called
• When to use
• Control a method’s behavior from a test to force the code down a specific path (ex. forcing a
method to throw an error in order to test error handling)
• prevent a specific method from being called directly (ex. prevent trigger XMLHttpRequest)
Creating a stub as an anonymous function
Tested Function
UT
Using a stub to wrap an existing method
Tested Function
UT
Using a stub to verify ajax result
Mock
• A fake function(like spy) with pre-programmed behavior(like stub) as well as pre-
programmed expectation
• Test will fail if not used as expected
• should have no more than one mock in a single test
• if you wouldn’t add an assertion for some specific call, don’t mock it. Use a stub
instead.
• When to use
• Use for external function, ex. third-party API
Mock Sample
Tested Function
UT
Comparison Among Spy/Stub/Mock in Sinon
What Validation When to Use
Spy A function that records arguments,
return value, the value of this and
exception thrown (if any) for all its
calls
N Record function behavior
Stub A Function(spy) with pre-
programmed behavior
N • prevent a specific method from
being called directly
• Control a method’s behavior
from a test to force the code
down a specific path
Mock A fake function(like spy) with pre-
programmed behavior(like stub) as
well as pre-programmed
expectation
Y Use for external function
Sinon Best Practice
• Wrap test function with sinon.test() to make sure that customized testing
behavior was recovered
• Use this to refer to sinon-related API, ex. this.spy
Good Practice for UT
Definition
• Unit Test
• check a single explicit assumption about one function
• the tested function should be open for external usage
• the tested function should isolate the external function to avoid implicit assumption
Identify Test Scenario
Write Unit Test Function
Implement Feature Function
Unit Test Development Flow
Quality of Good Unit Test
Readable Maintainable Trustworthy
1. Opaque Anti-Pattern
Problem: Magic Literals
Suggestion: No Magic Literals
Problem: Unclear Test Name
Informative, Consistent Test Name
• Naming rule:
• void nameOfFunctionUnderTest_ContextOfTest_DesiredResultOfTest()
• Context of test: how we set up the world to make sure that the assumption we want to
exercise run as expected
• Desired result: how we expect that assumption to behave
Suggestion : Informative, Consistent Test Name
Differentiate Arrange-Act-Assert Section
• Arrange: setting up the context to exercise the assumption
• Act: trigger tested function to execute(minimum is better)
• Assert: construct a value that we can test the assumption was exercise
correctly(including variable only using in assert section)
Differentiate Arrange-Act-Assert Section
Suggestion For Opaque Anti-Pattern
• No magic literals
• Consistent informative test name
• Differentiate Arrange-Act-Assert section
2. Wet Anti-Pattern
Don’t Repeat Yourself (Dry)
Use Helper Function
Keep Act Section in Visual
Suggestion For Wet Anti-Pattern
• Don’t Repeat Yourself (Dry)
• Use Helper Function
• Keep Act Section in Visual
3. Deep Anti-Pattern
Problem: More Than One Assert Per Test
• Test case fail when first assert fail, so don’t know the result of the rest of the
assert
More Than One Assert Per Test
Suggestion: One Assert Per Test
Clear Failure Test Result
Suggestion For Deep Anti-Pattern
• Avoid too many explicit assumptions per test
• Minimize assumptions per test
4. Wide Anti-Pattern
If one bug causes many tests fail, then it’s hard
to find the bug….
Ideal Scenario:
one bug cause one unit test fail that means we
can check production code directly without looking
unit test
Problem: Unexpected Implicit Assumption
Suggestion: Use Seam
Suggestion For Wide Anti-Pattern
• Avoid many implicit Assumption
• Create Seam(where that allows you to slide in instance of a fake type derived
from a real concrete type in production code) by inversion of control
Summary
• Respect unit test code as much as production code
• Test review (write once, read many times)
• Only one explicit assumption per test
• Minimize implicit assumptions per test
Reference
• https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
• https://martinfowler.com/bliki/TestPyramid.html
• http://sinonjs.org/
• https://www.youtube.com/watch?v=SvudHPTEsIk
• https://www.youtube.com/watch?v=qFaBHHg6RQU&t=2s
• https://www.youtube.com/watch?v=i_oA5ZWLhQc
• https://www.youtube.com/watch?v=iP0Vl-vU3XM

Writing good unit test