问题
I'm new to Spock and this question refers to the example on page 178 of Java Testing with Spock. The class under test is the Basket class for a shopping application, and the method of this class being tested is canShipCompletely()
public class Basket {
private WarehouseIneventory warehouseInventory;
private ShippingCalculator shippingCalculator;
protected Map<Product, Integer> contents = new HashMap<>();
...
public void addProduct(Product product) {
addProduct(product, 1);
}
public void addProduct(Product product, int times) {
if (contents.containsKey(product)) {
int existing = contents.get(product);
contents.put(product, existing + times);
} else {
contents.put(product, times);
}
}
public Boolean canshipCompletely() {
if(warehouseInventory.isEmpty()) return false;
try {
for (Entry<Product, Integer> entry : contents.entrySet())
boolean ok = warehouseInventory.isProductAvailable(
entry.getKey().getName(),
entry.getValue()
);
if (!ok) {
return false;
}
}
return true;
} catch (Exception e) {
return false;
}
...
}
This method canShipCompletely() loops over the items in the basket (in Map contents) and for each item, it makes a call to warehouseInventory.isProductAvailable(product, count) to see if there is sufficient stock in the warehouse to fill the order. The Warehouse class is a collaborator of the Basket class that is mocked in the following test
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 two products"
basket.addProduct tv
basket.addProduct camera
boolean readyToShip = basket.canShipCompletely()
then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _) >> true
0 * inventory.preload(_ , _)
}
The then: block verifies the boolean readyToShip is true, and that inventory.isProducAvailable() was called twice and inventory.preload() was not called at all. The next to last line is both checking behavior of the mock and telling it to return true for calls to isProductAvailable(). What I don't understand is the test will fail if I move the mock predefined response to the and: block as follows
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)
// ******** Move mock predefined response here **********
inventory.isProductAvailable( _ , _ ) >> true
basket.setWarehouseInventory(inventory)
when: "user checks out two products"
basket.addProduct tv
basket.addProduct camera
boolean readyToShip = basket.canShipCompletely()
then: "order can be shipped"
readyToShip
2 * inventory.isProductAvailable( _ , _)
0 * inventory.preload(_ , _)
}
The failure I get is too few calls to isProductAvailable():
Too few invocations for:
2 * inventory.isProductAvailable( _ , _) (1 invocation)
Unmatched invocations (ordered by similarity):
1 * inventory.isEmpty()
I don't understand why the predefined behavior for the mock can't be moved to the and: block.
回答1:
Please refer to the documentation
When mocking and stubbing the same method call, they have to happen in the same interaction. In particular, the following Mockito-style splitting of stubbing and mocking into two separate statements will not work:
setup:
subscriber.receive("message1") >> "ok"
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
As explained in Where to Declare Interactions, the receive call will first get matched against the interaction in the then: block. Since that interaction doesn’t specify a response, the default value for the method’s return type (null in this case) will be returned. (This is just another facet of Spock’s lenient approach to mocking.). Hence, the interaction in the setup: block will never get a chance to match.
来源:https://stackoverflow.com/questions/45360750/predefined-mock-response-in-spock