我是Spock的新手,这个问题是指使用Spock进行Java测试的第178页上的示例。被测试的类是购物应用程序的Basket类,正在测试的这个类的方法是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;
}
...
}
此方法canShipCompletely()循环播放篮子中的项目(在Map内容中),对于每个项目,它调用warehouseInventory.isProductAvailable(product,count)以查看仓库中是否有足够的库存来填充命令。 Warehouse类是Basket类的协作者,它在以下测试中被模拟
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(_ , _)
}
then:block验证布尔readyToShip是否为true,并且inventory.isProducAvailable()被调用了两次,并且根本没有调用inventory.preload()。倒数第二行是检查模拟的行为,并告诉它对isProductAvailable()的调用返回true。我不明白的是,如果我将模拟预定义响应移动到and:block,则测试将失败,如下所示
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(_ , _)
}
我遇到的失败是对isProductAvailable()的调用太少:
调用次数太少:
2 * inventory.isProductAvailable(_,_)(1次调用)
不匹配的调用(按相似性排序):
1 * inventory.isEmpty()
我不明白为什么mock的预定义行为无法移动到and:block。
答案 0 :(得分:0)
当模拟和存根相同的方法调用时,它们必须在相同的交互中发生。特别是,以下Mockito风格将存根和模拟分成两个单独的语句将不起作用:
setup:
subscriber.receive("message1") >> "ok"
when:
publisher.send("message1")
then:
1 * subscriber.receive("message1")
如在声明交互的位置中所解释的,接收调用将首先与then:块中的交互匹配。由于该交互未指定响应,因此将返回方法的返回类型的默认值(在本例中为null)。 (这只是Spock宽大的嘲弄方法的另一个方面。)因此,setup:block中的交互永远不会有机会匹配。