【问题标题】:Predefined mock response in SpockSpock 中预定义的模拟响应
【发布时间】:2018-01-03 18:30:33
【问题描述】:

我是 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() 循环遍历购物篮中的项目(在地图内容中),并且对于每个项目,它都会调用 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: 块验证布尔值 readyToShip 是否为真,并且 inventory.isProducAvailable() 被调用了两次,inventory.preload() 根本没有被调用。最后一行的下一行是检查模拟的行为并告诉它返回 true 以调用 isProductAvailable()。我不明白的是,如果我将模拟预定义响应移动到 and: 块,测试将失败,如下所示

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

我不明白为什么模拟的预定义行为不能移动到 and: 块。

【问题讨论】:

    标签: testing mocking spock


    【解决方案1】:

    请参考documentation

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

    setup:
    subscriber.receive("message1") >> "ok"
    
    when:
    publisher.send("message1")
    
    then:
    1 * subscriber.receive("message1")
    

    正如在哪里声明交互中所解释的,接收调用将首先与 then: 块中的交互进行匹配。由于该交互未指定响应,因此将返回方法返回类型的默认值(在本例中为 null)。 (这只是 Spock 对嘲笑的宽容态度的另一个方面。)。因此, setup: block 中的交互永远不会有机会匹配。

    【讨论】:

    • 这基本上是说如果您正在为模拟方法编写响应并验证该模拟方法,则必须在 then: 块中进行?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 1970-01-01
    • 2022-06-11
    相关资源
    最近更新 更多