In the Groovy world, I'm a big fan of the Spock specification framework and have written about it before. One little challenge I ran into was to setup an interaction for a mock to return a different result depending on how many times its been called. To illustrate what I'm talking about, I'll set things up with the following little scenario.
Let's say we have a class, RecordProcessor whose job is to read records from a source until there are no more records to be read. A collaborator of the class, RecordReader, is responsible for the actual record reading, and is the class whose behavior we want to mock.
The method to be called on the RecordReader takes as a parameter, the number of records to read and reports whether or not there are more records left in the source to be read. For simplicities sake, let's assume the responsibility of the RecordProcessor is to simply invoke the RecordReader until it reports that there are no more records to read.
class RecordProcessor {
RecordReader recordReader
int threshold
def process() {
boolean isProcessingComplete = true
boolean recordsRemaining = true
while(recordsRemaining) {
recordsRemaining = recordReader.readRecords(threshold)
}
// would do more processing evaluation here...
processingComplete
}
}
We want our unit test to determine whether or not the class under test can detect when there are no more records to read and report that processing was completed. Here is the base of the Spock specification where the mocking setup is left to a method:
class RecordProcessorSpec extends Specification {
def threshold = 5
RecordProcessor recordProcessor = new RecordProcessor(threshold: threshold)
def "can process all the records accessible by a record reader"() {
given:
recordProcessorHasMockRecordReader()
when:
isProcessingComplete = recordProcessor.process()
then:
isProcessingComplete
}
}
The wiki page describing mocking and interactions can be found My first pass at setting up the mock looked like the below, where the first two calls to readRecords method would return true and the third would return false (for more on how mocking is done with Spock, see Interactions).
def recordProcessorHasMockRecordReader() {
def mock = Mock(RecordReader)
2 * mock.readRecods(threshold) >> true
1 * mock.readRecods(threshold) >> false
recordProcessor.recordReader = mock
}
This didn't do the trick however as 3 invocations were detected for the first definition of how the method should behave:
Too many invocations for:
2 * mock.readFrom(threshold) >> true (3 invocations)
I found one answer in using a closure to define the return value of the method invocation that had a bound variable to tracking the number of reads that were asked for:
def recordProcessorHasMockRecordReader() {
def mock = Mock(RecordReader)
def readsAttempted = 0
3 * mock.readRecods(threshold) >> {
readsAttempted++
readsAttepted < 3 ? true : false
}
recordProcessor.recordReader = mock
}
With each invocation of the readRecords method by the RecordProcessor, the readsAttempted variable defined outside of the closure is incremented. With the first two calls, the RecordProcessor is told there are more records to process, and with the third call, it is told that all the records have been read.
Are there any other approaches anyone has found to approach a similar problem? Thanks.
To return true for the first two invocations and false afterwards, do:
ReplyDeletemock.readRecords(threshold) >>> [true, true, false]
Thanks Peter! That does the trick
ReplyDelete