Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spock mock returns null when mocking a class of spring stereotype "Repository" - Why?

I just found out that when using spock you can mock all spring stereotypes ("Component", "Service" and "Controller") except of "Repository". What is the reason?

Example:

The following example does not work as long as the "TestRepository" class has the stereotype annotation "Repository" because the return value of the mock is "null". If changing the stereotype annotation to one of the other stereotypes the mock returns the expected instance of "TestModel".

Controller:

@Controller
class TestController {

private final TestRepository testRepository

TestController(TestRepository testRepository) {
    this.testRepository = testRepository
}

@RequestMapping("/test")
String test(Model model) {
    TestModel testmodel = testRepository.getTestModel()
    ...
}

Repository: ("@Repository" does not work, "@Component", "@Service" and "@Controller" work)

@Repository
class TestRepository {
    TestModel getTestModel() {...}
}

Test:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc(secure = false)
@DirtiesContext
class TestControllerSpec extends Specification {
    @Autowired
    MockMvc mockMvc

    @Autowired
    TestRepository testRepository

    def "test"() {
        testRepository.getTestModel >> new TestModel()
    }

    @TestConfiguration
    static class MockConfig {
        def factory = new DetachedMockFactory()
        @Bean
        @Primary
        TestRepository testRepository() {
            factory.Mock(TestRepository)
        }
    }
}
like image 255
Patrick Roos Avatar asked Jan 09 '18 15:01

Patrick Roos


2 Answers

See Spock Issue 758 for more details, in short @Repository instances are wrapped in by spring in a proxy to handle transactions, this breaks the mock interactions setup. You can either use Spock 1.2 (currently SNAPSHOT only) and the new annotation @UnwrapAopProxy on the testRepository field. Or you can use this function to unwrap it manually.

public static <T> T getTargetObject(Object proxy) throws Exception {
    if (AopUtils.isAopProxy(proxy)) {
        return (T) ((Advised) proxy).getTargetSource().getTarget();
    } else {
        return (T) proxy;
    }
}


def "test"() {
    given:
    TestRepository mock = getTargetObject(testRepository)
    mock.getTestModel() >> new TestModel()

    expect:
    testRepository.getTestModel() != null
}
like image 82
Leonard Brünings Avatar answered Nov 06 '22 11:11

Leonard Brünings


The full source for a Spock 1.2-SNAPSHOT version:

@SpringBootTest
@AutoConfigureMockMvc
class TestControllerSpec extends Specification {
    @Autowired
    MockMvc mockMvc

    @SpringSpy
    @UnwrapAopProxy
    TestRepository testRepository

    def "test"() {
        when:
        //def result = mockMvc.get(...) //implement me

        then:
        1 * testRepository.getTestModel >> new TestModel()

        result
    }
}
like image 3
Jeff Sheets Avatar answered Nov 06 '22 09:11

Jeff Sheets