Spock中的预定义模拟响应 [英] Predefined mock response in Spock

查看:97
本文介绍了Spock中的预定义模拟响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Spock的新手,这个问题涉及Java测试with Spock的第178页上的示例.被测试的类是购物应用程序的Basket类,而被测试的类的方法是canShipCompletely()

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;
    }

  ...
}

此方法canShipCompletely()遍历购物篮中的物品(在地图内容中),并且对于每个物品,它都会调用WarehouseInventory.isProductAvailable(product,count)以查看仓库中是否有足够的库存来填充命令. Warehouse类是Basket类的协作者,在以下测试中被模拟

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(_ , _)
}

then:块将验证布尔readyToShip是否为true,并且两次调用了stock.isProducAvailable(),而根本没有调用过stock.preload().最后一行是检查模拟的行为,并告诉它对isProductAvailable()的调用返回true.我不明白的是,如果我将模拟预定义响应移动到and:块,则测试将失败,如下所示:

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(_ , _)
}

我遇到的失败是对isProductAvailable()的调用太少了:

The failure I get is too few calls to isProductAvailable():

的以下调用很少:

2 *库存.isProductAvailable(_,_)(1次调用)

2 * inventory.isProductAvailable( _ , _) (1 invocation)

不匹配的调用(按相似性排序):

Unmatched invocations (ordered by similarity):

1 * stock.isEmpty()

1 * inventory.isEmpty()

我不明白为什么模拟的预定义行为无法移至and:块.

I don't understand why the predefined behavior for the mock can't be moved to the and: block.

推荐答案

请参考

在模拟和存根相同的方法调用时,它们必须在相同的交互中发生.特别是,以下将Mockito样式的存根和模拟拆分为两个单独的语句的方法将不起作用:

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")

如在哪里声明交互中所述,接收调用将首先与then:块中的交互匹配.由于该交互未指定响应,因此将返回该方法的返回类型的默认值(在这种情况下为null). (这只是Spock宽大的嘲笑方法的另一个方面.)因此,setup:块中的交互将永远不会有匹配的机会.

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.

这篇关于Spock中的预定义模拟响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆