You are currently viewing Spock: How to get Mock to return value to avoid NullPointerException or InvalidSpecException

Spock: How to get Mock to return value to avoid NullPointerException or InvalidSpecException

The following guide explains how to use mock in Spock Testing Framework in a proper way to get rid of unnecessary problems like NullPointerExceptions and InvalidSpecException when you try stubbing instead of mocking.

The problem

Let’s suppose that we have ShipmentService with delivery metod like below and we would like to test it. As you may see the delivery method calls OrderService to get delivery metod selected for the specific order and then it calls orderService.setDeliveryStatus with different deliveryStatus depends on the deliveryMethod.

public class ShipmentService {
    private final OrderService orderService;

    public ShipmentService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void deliver(String orderId) {
        String deliveryMethod = orderService.getDeliveryMethod(orderId);
        if (deliveryMethod.equals("CAR")) {
            orderService.setDeliveryStatus("Delivered by CAR");
        } else {
            orderService.setDeliveryStatus("Unsupported delivery method");
        }
    }
}
public interface OrderService {
    String getDeliveryMethod(String orderId);

    void setDeliveryStatus(String deliveryMethod);
}

Let’s try to create Spock test. When you run the following test you got NullPointerException in ShipmentService.java:10, because mocked orderService.getDeliveryMethod(orderId) returns null.

import spock.lang.Specification

class ShipmentServiceTest extends Specification {
  def "delivery should set 'Delivered by CAR' status when order's delivery method is CAR"() {
    given:
      OrderService orderService = Mock()
      ShipmentService shipmentService = new ShipmentService(orderService)
    when:
      shipmentService.deliver("1")
    then:
      1 * orderService.getDeliveryMethod("1")
      1 * orderService.setDeliveryStatus("Delivered by CAR")
  }
}

Watch the solution on YouTube

Wrong solution

The first thought to deal with it may be to use Stub instead of Mock, but it’s a dead end. Why? Let’s run the code below to find out.

import spock.lang.Specification

class ShipmentServiceTest extends Specification {
  def "delivery should set 'Delivered by CAR' status when order's delivery method is CAR"() {
    given:
      OrderService orderService = Stub()
      orderService.getDeliveryMethod("1") >> "CAR"
      ShipmentService shipmentService = new ShipmentService(orderService)
    when:
      shipmentService.deliver("1")
    then:
      1 * orderService.getDeliveryMethod("1")
      1 * orderService.setDeliveryStatus("Delivered by CAR")
  }
}

Of course we got InvalidSpecException like below, because we cannot use Stub for testing interaction.

org.spockframework.runtime.InvalidSpecException: Stub 'orderService' matches the following required interaction:

1 * orderService.getDeliveryMethod("1")   (0 invocations)

Remove the cardinality (e.g. '1 *'), or turn the stub into a mock.

Proper solution

The right solution of the problem is to add return value “CAR” to place when we test the first interaction. It means that we expect exactly once invocation of orderService.getDeliveryMethod with argument “1” which should return mocked value “CAR” instead if null. More you can find in the official Spock documentation.

import spock.lang.Specification

class ShipmentServiceTest extends Specification {
  def "delivery should set 'Delivered by CAR' status when order's delivery method is CAR"() {
    given:
      OrderService orderService = Mock()
      ShipmentService shipmentService = new ShipmentService(orderService)
    when:
      shipmentService.deliver("1")
    then:
      1 * orderService.getDeliveryMethod("1") >> "CAR"
      1 * orderService.setDeliveryStatus("Delivered by CAR")
  }
}

Bonus: How to parametrize the test to cover all of the conditions

In our case we can introduce orderDeliveryMethod and expectedDeliveryStatus parameters to paramtertize test input data and the expectation.

import spock.lang.Specification

class ShipmentServiceTest extends Specification {
  def "delivery should set '#expectedDeliveryStatus' status when order's delivery method is '#orderDeliveryMethod'"() {
    given:
      OrderService orderService = Mock()
      ShipmentService shipmentService = new ShipmentService(orderService)
    when:
      shipmentService.deliver("1")
    then:
      1 * orderService.getDeliveryMethod("1") >> orderDeliveryMethod
      1 * orderService.setDeliveryStatus(expectedDeliveryStatus)
    where:
      orderDeliveryMethod || expectedDeliveryStatus
      "CAR"               || "Delivered by CAR"
      "SHIP"              || "Unsupported delivery method"
  }
}

That’s all what I’ve prepared for you in this tutorial, if I helped you, please consider sharing this post to help me gain a wider audience.
Thanks and I hope to see you in my next tutorial.

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments