MikeHT (137) [Avatar] Offline
#1
I'm a little confused on the point of this code. Is it just that we can program our mock in the then: block? Also, the original code for this test is:

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


I would think we could program the mock just after we create it and get the same results:

	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)

		inventory.isProductAvailable( _ , _ ) >> true                // PROGRAM BEHAVIOR HERE

		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( _ , _)                      // NOTE REMOVED >> true
		0 * inventory.preload(_ , _)
	}


However, when I do this the test fails with the message that there were too few invocations to isProductAvailable(). I don't understand why.
mhalver (33) [Avatar] Offline
#2
I ran into this a few times myself when trying to use Mocks. I'm not sure off-hand if it is addressed in the book, but it sort of is (although vaguely) in the online documentation. Basically you have to set the behavior and do the test for the number of invocations at the same time. The online documentation mentions that behavior set in the then block takes priority.

When you do

2 * inventory.isProductAvailable(_,_)


you are not specifying a return value, and thus are agreeing to use the default value (which are basically the "zero" values - null for objects, false for booleans, etc).

Because the then block takes priority, the return value you specified in your given block is ignored during the when block. When the first item is missing, the Basket returns false from ::canShipCompletely and doesn't bother checking the other item (so ::isProductAvailable is only called once).

When the return value is defined at the same time as the invocation count check, everything works because the return value you specified is used. You actually can move that entire line up into the given block (according to the online documentation, this is actually what spock does when it is in the then block), although it feels a little weird to be defining how many invocations you expect to see in the given block. You just can't break it up into two parts.
MikeHT (137) [Avatar] Offline
#3
A friend told me this is actually addressed, though somewhat indirectly, in chapter 8. I haven't quite gotten there yet. But from what I can determine, if you are setting behavior for a mocked collaborator and testing that behavior (e.g. seeing how many times the method is called), you must do so in the then: block.
mhalver (33) [Avatar] Offline
#4
MikeHT wrote:A friend told me this is actually addressed, though somewhat indirectly, in chapter 8. I haven't quite gotten there yet. But from what I can determine, if you are setting behavior for a mocked collaborator and testing that behavior (e.g. seeing how many times the method is called), you must do so in the then: block.


You can do it in the given block, you just have to do both at the same time.
Kostis Kapelonis (63) [Avatar] Offline
#5
Hello

You are correct, that this is confusing for most Java developers because that is how Mockito works. In Mockito first you setup the mock and then the expectation, while in Spock you them
in one step at the end.

This is actually explained in chapter 6 in section 6.3.4 with the following footnote

"This is a big difference with Mockito. In Mockito you can separately stub a mock and verify it in another statement. In Spock you do both things at the same time."