2
votes

I'm creating a java app for raspberry pi using Pi4j.

The application fails to run out of raspbian OS due to lack of GPIO drivers in place. That's why when I run my spock integration tests I need to mock/stub a bean which relies on original Pi4j API. My problem is that the bean itself is mocked but the stubbed method returns null instead of the value I define.

@SpringBootTest
class RoboApplicationTest extends Specification {

    @Autowired
    ApplicationContext context

    @SpringBean
    GpioController gpioController = Stub()

    def "test context loads"() {
        given:
        gpioController.provisionDigitalInputPin(_, _, _) >> { throw new RuntimeException("I'm stubbed!") }

        expect:
        context != null
        context.containsBean('gpioController')
    }
}

Instead of throwing an exception or returning anything I put there "provisionDigitalInputPin" method returns null that causes further NPE.

You can find the whole app here: https://github.com/ahlinist/raspberry-pi4j/tree/feature/integration-tests

I'm trying to achieve the result described here: https://github.com/spockframework/spock/blob/master/spock-spring/boot-test/src/test/groovy/org/spockframework/boot/SpringBeanIntegrationSpec.groovy

I've tried many approaches with no result. Similar test with Mockito works well. What am I missing?

Update: To run integration tests execute:

./gradlew integrationTest

Update 2: The corresponding Spock ticket #1084 has been created on GitHub.

1
@kriegaex so you know how to make it work? - Anton Hlinisty
OMG, I posted this comment under the wrong question when I was going through my list of questions to follow up on. Wrong browser tab, sorry for the noise. I am going to delete this comment and the previous one again. - kriegaex

1 Answers

0
votes

When I clone your project and run the test, I also see a NPE, ...

java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:778) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    (...)
Caused by: java.lang.NullPointerException: null
    at robot.controller.impl.RaspberryPiInputImpl.addListener(RaspberryPiInputImpl.java:19) ~[main/:na]
    at robot.sensor.AbstractSensor.init(AbstractSensor.java:21) ~[main/:na]
    at robot.TwoWheelRobot.init(TwoWheelRobot.java:20) ~[main/:na]
    at robot.RoboApplication.run(RoboApplication.java:26) ~[main/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:775) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
    ... 61 common frames omitted

... but not in your test but in this class:

package robot.controller.impl;

import com.pi4j.io.gpio.GpioPinDigitalInput;
import com.pi4j.io.gpio.event.GpioPinListenerDigital;
import lombok.RequiredArgsConstructor;
import robot.controller.Input;
import robot.controller.Listener;

@RequiredArgsConstructor
public class RaspberryPiInputImpl implements Input {

    private final GpioPinDigitalInput gpioPinDigitalInput;

    @Override
    public void addListener(Listener listener) {
        GpioPinListenerDigital gpioListener = (GpioPinListenerDigital) listener;
        gpioPinDigitalInput.addListener(gpioListener);
    }
}

You have a final GpioPinDigitalInput field there which never gets initialised, i.e. it will always be null. So what else than a NPE do you expect when calling the addListener(..) on it? Maybe you should inject a value there...

Besides, I had to move the test from src/integration-test to src/test, otherwise Gradle would not run it and IntelliJ IDEA also not recognise it as a test class.