Extreme mocking
Mattia Tommasone
@raibaz
About
- 10+ years spent writing (and reading) code
- FLOSS Enthusiast
- Former:
- Developer Advocate @ Google
- Kids' football coach
- University lecturer
- Conference speaker
- Current:
- Lead developer @ Brandon Group
axiom 1: tests are vital to every software project
Axiom 1.1: every component in a software project should be described by tests
Disclaimer #1
class Adder {
fun add(op1: Int, op2: Int) {
return op1 + op2
}
}
however, in the real world...
- Database connections
- HTTP connections
- External services
- Third party library dependencies
- Non-trivial component interactions
- Side effects
class BusinessLogicService(
private val persistenceService: MyPersistenceService
) {
fun doSomethingRelevant(objectId: Long, someValue: Int) {
val myObject = persistenceService.retrieveObject(objectId)
?: throw IllegalArgumentException("$objectId not found")
myObject.someProperty = someValue
persistenceService.saveObject(myObject)
}
}
- You may not have a place to persists objects to
- Making sure objects are stored and retrieved correctly is outside of the scope of this component
why is testing this component hard?
well, then maybe i can change my component
class BusinessLogicService(
private val persistenceService: MyPersistenceService
) {
fun doSomethingRelevant(myObject: MyObjectClass, someValue: Int) {
myObject.someProperty = someValue
return myObject
// And leave it to someone else to care about persistence
}
}
but this is just moving the problem somewhere else
not really addressing it
- You may not necessarily be able to change the semantics of a component
- Someone (you) will still have to make sure the persistence layer and the business logic interact properly
why is this not a solution?
class BusinessLogicService(
private val persistenceService: MyPersistenceService
) {
fun doSomethingRelevant(objectId: Long, someValue: Int) {
val myObject = persistenceService.retrieveObject(objectId)
?: throw IllegalArgumentException("$objectId not found")
myObject.someProperty = someValue
persistenceService.saveObject(myObject)
}
}
what exactly do we want to test here?
-
Error conditions:
- Calling this method on a non-existing object should fail
- The "happy path": this component updates the requested object with the requested value
problem statement
- We need a way to drive the behavior of the persistence layer
- We need a way to perform assertions on how our component interacts with the persistence layer
disclaimer #2
mockk.io
class BusinessLogicServiceTest {
@MockK
private lateinit var persistenceService : MyPersistenceService
private lateinit var service : BusinessLogicService
@Before
fun setup() {
MockKAnnotations.init(this)
service = BusinessLogicService(persistenceService)
}
@Test
fun `doing something with a non-existing object throws an exception`() {
every {
persistenceService.retrieveObject(any())
} returns null
assertFailsWith<IllegalArgumentException> {
service.doSomethingRelevant(123L, 345)
}
}
}
@Test
fun `doing something with an existing object updates it and saves it`() {
every {
persistenceService.retrieveObject(any())
} returns MyClass()
service.doSomethingRelevant(123L, 345)
verify {
persistenceService.saveObject(any())
}
}
How do we test that the value being saved has actually been updated?
@Test
fun `doing something with an existing object updates it and saves it`() {
every {
persistenceService.retrieveObject(any())
} returns MyClass()
service.doSomethingRelevant(123L, 345)
val slot = slot<MyClass>()
verify {
persistenceService.saveObject(capture(slot))
}
assertEquals(345, slot.captured.someProperty)
}
Some glossary
- Strict mocking vs. non-strict (or relaxed) mocking
- Mocks vs. Stubs vs. Spies
- Argument matching
why exactly is this so powerful?
we can test interactions, i.e. contracts between components
we can test logic that interacts with stuff that may not be available in a CI environment
mocking dependencies can help expose bad design
It helps obtain command-query separation
it's not only about object-oriented code
how does this apply to my project?
phake.readthedocs.io
docs.python.org/3/library/unittest.mock.html
Extreme Mocking
By Mattia Tommasone
Extreme Mocking
- 246