Stubs are great when your class under test already has methods that allow you to understand whether everything works as expected (such as the canShipCompletely() method of the basket class). But most of the time, the only way to understand what happened during the unit test is to have a “log” of what methods were called along with their arguments and their responses.
Mocks are the answer to this need. By mocking a collaborator of the class under test, you not only can preprogram it with canned responses, but also can query it (after the unit test has finished) about all its interactions.
Listing 6.12 Stubbing responses with other stubs
Spock has a huge range of options when it comes to mocks
Spock supports many features when it comes to mocking. Some are more useful than others, some apply only to extreme cases, and some are so confusing that I avoid them on purpose. This chapter shows the features I find useful (I’ve left out about 10% of Spock features). You can always consult the official Spock website as a ref- erence.
Stub that will be used by the class under test Stubbing of intermediary class Instructing a
stub to return another stub
Using the parent stub in the class under test
6.3.1 All capabilities of stubs exist in mocks as well
The first thing to get out of the way is that mocks are a superset of stubs. All code list- ings I’ve shown you so far will work even if you use a mock. As an example, here’s list- ing 6.2 written with a mock this time. Apart from a single line, the rest of the code is exactly the same.
def "If warehouse is empty nothing can be shipped"() { given: "a basket and a TV"
Product tv = new Product(name:"bravia",price:1200,weight:18) Basket basket = new Basket()
and:"an empty warehouse"
WarehouseInventory inventory = Mock(WarehouseInventory) inventory.isEmpty() >> true
basket.setWarehouseInventory(inventory) when: "user checks out the tv"
basket.addProduct tv
then: "order cannot be shipped"
!basket.canShipCompletely() }
In Spock, you use stubs when you want to denote that the fake class you’re going to use will come with only preprogrammed behavior and its interactions won’t be veri- fied. Of the two listings, the semantically correct is 6.2 (with the stub) because the warehouse inventory is never queried at the end of the unit test for its interactions with the basket class.
Spock enforces this convention, so although mocks will work in the place of stubs, the opposite doesn’t apply. Attempting to use a stub in place of a mock will throw an error when Spock runs the unit test.
6.3.2 Simple mocking—examining whether a method was called
Let’s add another collaborator class in the electronic basket example. In the following listing, you’ll add the capability to charge credit cards.
public class Customer { private String name;
private boolean vip = false;
private String creditCard;
[...getters and setters here...]
}
public enum CreditCardResult {
OK, INVALID_CARD, NOT_ENOUGH_FUNDS;
Listing 6.13 Stubbing mocks
Listing 6.14 Java skeleton for credit card charging
Create a mock.
Instruct the mock to return true when isEmpty() is called.
Inject the mock into the class under test.
This method calls the mock behind the scenes.
Simple object for customer Credit card
number
Possible results from charging a credit card
175 Mocks: verifying values returned from the class under test
}
public interface CreditCardProcessor {
CreditCardResult sale(int amount, Customer customer);
void shutdown();
}
public class BillableBasket extends Basket{
private CreditCardProcessor creditCardProcessor;
public void setCreditCardProcessor(CreditCardProcessor creditCardProcessor) {
this.creditCardProcessor = creditCardProcessor;
}
public void checkout(Customer customer) {
[...code redacted..]
} }
The credit card system is implemented by an external library (imagine that you don’t even have the source code). Reading its API documentation, you see a big warning: its developers explain that the shutdown() method must be called whenever a credit card charge happens.7
Your job is to write a unit test that verifies the call of this method by the basket class without charging a credit card. You could get away with a stub if the credit card pro- cessor had a method named shutdownWasCalled()! But it doesn’t.
You can use a mock instead of a pure stub, as shown in the following listing.
def "credit card connection is always closed down"() { given: "a basket, a customer and a TV"
Product tv = new Product(name:"bravia",price:1200,weight:18) BillableBasket basket = new BillableBasket()
Customer customer = new
Customer(name:"John",vip:false, creditCard:"testCard") and: "a credit card service"
CreditCardProcessor creditCardSevice = Mock(CreditCardProcessor) basket.setCreditCardProcessor(creditCardSevice)
when: "user checks out the tv"
basket.addProduct tv
basket.checkout(customer) then: "connection is always closed at the end"
1 * creditCardSevice.shutdown() }
7 Otherwise, the world will explode.
Listing 6.15 Verification of a mocked method
Interface provided by an external system Charging
method
Must be called after each charge
Setter injection
Triggers credit card injection
Creating a mock from an interface
Injecting the mock into the class under test This method
calls the mock behind
the scenes. Verifying called
method
The important code line of this listing is the last one. Unlike all previous Spock tests, it doesn’t contain a standard assert statement (checked according to Groovy truth). This line is special Spock syntax and comes in this format:
N * mockObject.method(arguments)
When Spock sees this line, it makes the test pass only if that method on the mock has been called N times with the arguments provided (which can also be argument match- ers, as with stubs).
The last line in the listing means, “After this test is finished, I expect that the num- ber of times the shutdown() method was called is once.” The test will pass if this sen- tence is true and will fail in any other case.
Assume that with that unit test in place, a developer introduces a bug in the basket class that calls the shutdown() method two times. Spock will instantly fail the test with the error message shown in figure 6.3.
Spock knows the exact invocations of all mocks because during the test, it has replaced the classes with its own proxies that record everything.
6.3.3 Verifying order of interactions
With the mock for the credit card processor in place, you can ensure that the credit card service is shut down after the transaction (and without charging a real credit card). But listing 6.13 misses the sequence of calls. How do you know that the shut- down() method is called at the end, and not before the credit card charge step (which would be an obvious bug)? Listing 6.13 doesn’t cover this scenario.
Your first impulse, to check the order of calls that happen with the credit card ser- vice, would be to write something like this:
then: "credit card is charged and CC service is closed down"
1 * creditCardSevice.sale(1200,customer) 1 * creditCardSevice.shutdown()
Figure 6.3 Spock fails the test when the mocked method is called twice.
177 Mocks: verifying values returned from the class under test
This won’t work as expected. Spock doesn’t pay any attention to the order of verifica- tions inside a specific then: block. The preceding unit test will always pass, regardless of the exact sequence of events (if both of them are correct on their own).
The correct unit test needs to exploit the fact that multiple then: blocks are checked in order by Spock,8 as shown in the following listing.
def "credit card connection is closed down in the end"() { given: "a basket, a customer and a TV"
Product tv = new Product(name:"bravia",price:1200,weight:18) BillableBasket basket = new BillableBasket()
Customer customer = new
Customer(name:"John",vip:false, creditCard:"testCard") and: "a credit card service"
CreditCardProcessor creditCardSevice = Mock(CreditCardProcessor basket.setCreditCardProcessor(creditCardSevice)
when: "user checks out the tv"
basket.addProduct tv basket.checkout(customer)
then: "credit card is charged and"
1 * creditCardSevice.sale( _, _) then: "the credit card service is closed down"
1 * creditCardSevice.shutdown() }
Notice that in this test, you want to focus on the order of events and nothing else, so you’ve used unconditional argument matchers for the arguments of the sale() method because you don’t care about them in this test. (Usually, there should be another unit test focusing on them.)
6.3.4 Verifying number of method calls of the mocked class
If you already have significant experience with other mocking frameworks,9 you should have noticed something strange in listings 6.13 and 6.14. In listing 6.14, you’re clearly noting to Spock that you expect the sale() method to be called. But listing 6.13 men- tions nothing about the sale() method. How does the test in listing 6.13 pass?
It turns out that mocks and stubs created by Spock are lenient by default. The test will fail only if the behavior of the mocks is contained in the then: block against your explicit instructions. Calling a method of a mock that was never mentioned has no neg- ative effect. Not calling a stubbed/mocked method also doesn’t affect the unit test.
Listing 6.16 Verification of a specific order of mocked methods
8 This was also shown in chapter 4.
9 And have been paying close attention to the code listings.
Creation of a mock from an interface
First this verification will be checked.
This will only be checked if the first verification passes.
When you call a mocked method that doesn’t have explicit stubbing instructions, Spock will return default values (false for Boolean variables, 0 for numbers, and null for objects). If you want to make sure that a method isn’t called in a mock, you have to declare it in the then: block as well. Pay close attention to the last statement of the fol- lowing code listing.
def "Warehouse is queried for each product"() { given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory) basket.setWarehouseInventory(inventory)
when: "user checks out both products"
basket.addProduct tv basket.addProduct camera
boolean readyToShip = basket.canShipCompletely() then: "order can be shipped"
readyToShip 2 * inventory.isProductAvailable( _ , _) >> true
0 * inventory.preload(_ , _) }
There are three important points to notice in listing 6.17 that relate to the three lines in the then: block.
Starting from the bottom, you want to make sure that the basket only queries the warehouse, but never tampers with the stock levels. Therefore, the code explicitly states that you expect zero invocations for the method that fills the inventory.
The middle line verifies that the method of product availability is called twice (because the test deals with two products). Because you want the basket to think that the warehouse is full, you also stub the method to return true both times. Thus the code in this line is both a mock expectation and a predefined stubbed response:10
2 * inventory.isProductAvailable( _ , _) >> true
This line says to Spock, “After this test is finished, I expect that the method isProductAvailable() was called twice. I don’t care about the arguments. But when it’s called, please return true to the class that called it.”
The last thing to notice is that unlike previous code listings, the canShip- Completely() method is called in the when: block, and only its result is checked in
Listing 6.17 Explicit declaration of interactions
10This is a big difference from Mockito. In Mockito, you can separately stub a mock and verify it in another state- ment. In Spock, you do both things at the same time.
Creating a mock/stub
Mocks are only checks in the when: block.
Stubbing a mocked method
Verifying that a method was never called
179 Mocks: verifying values returned from the class under test
the then: block. The reason for this is that Spock records the interactions of mocks in the when: block (which should always contain the trigger code). When using mocks (or stubs), the then: block must contain only verifications.
6.3.5 Verifying noninteractions for multiple mocked classes
Now you know how to verify individual methods for any number of invocations. But sometimes you want to cast a wider net, and control invocations at the class level instead of the method level. The underscore character is flexible regarding its posi- tion inside a verification statement. Consider the following listing.
def "Warehouse is queried for each product - strict"() { given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory) basket.setWarehouseInventory(inventory)
when: "user checks out both products"
basket.addProduct tv basket.addProduct camera
boolean readyToShip = basket.canShipCompletely() then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _) >> true 1 * inventory.isEmpty() >> false 0 * inventory._
}
Here you’ve written a strict unit test because it assumes that regardless of the number of methods that exist in the inventory class, the basket class should call only isProductAvailable() and isEmpty() and nothing else. Therefore, the last verifica- tion line uses the underscore as a method matcher:
0 * inventory._
This line means, “I expect zero invocations for all other methods of the inventory class.” Be careful when using this technique because it means that you know exactly the interface between the class under test and the mock. If a new method is added in the mock (in the production code) that’s used by the class under test, this Spock test will instantly fail.
If you have multiple mocks, you can write even stricter tests by placing the under- score as a class name, as shown in the next listing.
Listing 6.18 Verifying interactions for all methods of a class
Setting expectations for specific methods Setting
expectations for all other methods of the class
def "Only warehouse is queried when checking shipping status"() { given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory) basket.setWarehouseInventory(inventory)
ShippingCalculator shippingCalculator = Mock(ShippingCalculator) basket.setShippingCalculator(shippingCalculator)
when: "user checks out both products"
basket.addProduct tv basket.addProduct camera
boolean readyToShip = basket.canShipCompletely() then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _) >> true
_ * inventory.isEmpty() >> false 0 * _
}
In this code listing, the basket class is injected with two mocks (one for shipping costs and one for the inventory). After running the test, you want to verify that only two spe- cific methods were called on the inventory and that nothing was called for the ship- ping cost service. Instead of manually declaring all other methods with zero cardinality one by one, you use the underscore character in the class part of the verifi- cation. In Spock, the line
0 * _
means, “I expect zero invocations for all other methods of all other classes when the test runs.” Also notice that you don’t care how many times the isEmpty() method is called, and you use the underscore operator in the cardinality:
_ * inventory.isEmpty() >> false
This line means, “I expect the isEmpty() method to be called any number of times, and when it does, it should return false.”
Listing 6.19 Verifying noninteractions for all mocks
The many faces of the underscore character
As you may have noticed by now, the underscore character is a special matcher for Spock tests. In the basic form of a mock verification, N * class.method(argument), the underscore can be used to match arguments (listings 6.16, 6.17), methods (listing 6.18), classes, and even the cardinality N (listing 6.19). For all these cases, you don’t care about the respective part of the verification.
Underscore matches arguments.
Underscore matches number of invocations.
Underscore matches mocked classes.
181 Mocks: verifying values returned from the class under test
6.3.6 Verifying types of arguments when a mocked method is called
I’ve shown how to verify specific arguments in mock invocations and how to say to Spock that you don’t care about arguments (the underscore character). But between these two extremes, you can verify several other attributes of arguments. One of the most useful verifications is to make sure that the argument passed isn’t null. This can be described naturally in Spock, as shown in the next listing.
def "Warehouse is queried for each product - null "() { given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) Basket basket = new Basket()
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory) basket.setWarehouseInventory(inventory)
when: "user checks out both products"
basket.addProduct tv basket.addProduct camera
boolean readyToShip = basket.canShipCompletely() then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable(!null ,1) >> true }
In this listing, you want to make sure that whatever argument is passed to the inven- tory isn’t null (because the arguments should be names of products). For the second argument, where you know exactly what will be used, you directly put in the value:
2 * inventory.isProductAvailable(!null ,1) >> true
This line means, “I expect that the method isProductAvailable() will be called twice. The first argument can be anything apart from null, and the second argument will always be 1. When that happens, the method will return true.”
In unit tests with complex class hierarchies, you can verify the type of arguments as well. The following listing illustrates this (for this trivial example, verifying the type of arguments is probably overkill).
def "Warehouse is queried for each product - type "() { given: "a basket, a TV and a camera"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) Basket basket = new Basket()
Listing 6.20 Verifying that arguments aren’t null when a mocked method is called
Listing 6.21 Verifying the type of arguments
Creating a Spock mock
Verifying that the first argument isn’t null
and: "a warehouse with limitless stock"
WarehouseInventory inventory = Mock(WarehouseInventory) basket.setWarehouseInventory(inventory)
when: "user checks out both products"
basket.addProduct tv basket.addProduct camera
boolean readyToShip = basket.canShipCompletely() then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable(_ as String ,_ as Integer) >> true }
Again you use the magic underscore character, this time combined with the as key- word. Notice that a null argument will also fail the verification so the as/underscore combination includes the null check.
6.3.7 Verifying arguments of method calls from mocked classes
Using the underscore character as an argument in your mock verifications means that you don’t care about the argument at all. But what happens if your unit test is focused on the arguments and you do care?
In that case, my advice is to declare exactly what you expect. You’ve already seen that with scalar values, you use them directly as arguments. The same thing happens with full objects, as shown in the next listing.
def "vip status is correctly passed to credit card - simple"() { given: "a basket, a customer and a TV"
Product tv = new Product(name:"bravia",price:1200,weight:18) Product camera = new Product(name:"panasonic",price:350,weight:2) BillableBasket basket = new BillableBasket()
Customer customer = new
Customer(name:"John",vip:false,creditCard:"testCard") and: "a credit card service"
CreditCardProcessor creditCardSevice = Mock(CreditCardProcessor) basket.setCreditCardProcessor(creditCardSevice)
when: "user checks out two products"
basket.addProduct tv basket.addProduct camera basket.checkout(customer) then: "credit card is charged"
1 * creditCardSevice.sale(1550, customer) }
As you can see in this listing, there’s no special syntax for objects:
1 * creditCardSevice.sale(1550, customer)
Listing 6.22 Verifying exact arguments of a mocked method
Creating a Spock mock
Verifying that the first argument is always a string and the second always an integer
Creating a Spock mock
Verifying that the second argument is equal to a specific object instance