Search code examples
spring-bootgroovymockingspock

How can I mock part of a Spring Boot service for ALL of my Spock Integration tests?


I am using Spock to create integration tests for my Spring Boot Application, but I'm having trouble figuring out how to mock part of a Service for all my test cases.

My app writes to an Azure Cosmos database running in Gremlin Graph mode, and since I don't know of any in-memory databases to adequately simulate it, I would like to make sure any service that writes to my development database only interacts with entities labeled with the current test's random UUID. So any service that reads/writes to the database needs to make sure to include the current test ID in any query.

What's the best way to mock a couple reused methods in an abstract base class via Spock?

GraphService.groovy <== The abstract class with some methods I wish to mock.

abstract class GraphService {
    @Autowired 
    protected GraphTraversalSource g

    protected GraphTraversal buildBaseQuery() {
        return g.V()
    } 

    protected GraphTraversal buildBaseCreationQuery(String label) {
        return g.addV(label)
    }
}

Several database searching/modifying services inherit the above class. For all of my tests, instead of g.V() I want g.V().has("testID", testId) and instead of g.addV(label) I want g.addV(label).property("testID", testId). How can I accomplish this across all my integration tests? I tried creating a base-specification class that specified this behavior, but it didn't work.

TestConfig.groovy

@Configuration
class TestConfig {
    @Bean
    @Primary
    GraphPersistenceService persistenceService(
            GraphTraversalSource g) {
        DetachedMockFactory mockFactory = new DetachedMockFactory()
        GraphPersistenceService persistenceService = mockFactory.Stub( //Mock doesn't work either
            [constructorArgs: [g]], GraphPersistenceService)
        return graphPersistenceService
    }
}

BaseIntegrationTest.groovy

class BaseIntegrationTest extends Specification {
    @Autowired
    TestUtil testUtil

    @Autowired
    GraphTraversalSource g

    @Autowired
    GraphPersistenceService persistenceService

    def setup() {
        persistenceService.buildBaseQuery >> g.V().has("testID", testUtil.id)
        persistenceService.buildBaseCreationQuery(_ as String) >> { label ->
            g.addV(label).property("testID", testUtil.id)
        }
    }

    def cleanup() {
        testUtil.removeAllEntitiesWithCurrentTestId()
    }
}

And then in an actual test:

@SpringBootTest(classes = MyGraphApplication.class)
@ContextConfiguration(classes = [GraphDbConfig.class, TestConfig.class])
@ActiveProfiles("test")
@TestPropertySource(locations = 'classpath:application-local.properties')
class UserOfPersistenceServiceSpec extends BaseIntegrationTest {
    @Autowired
    UserOfGraphPersistenceService userOfPersistenceService

    def "Can create a bunch of vertices"() {
        expect:
        userOfPersistenceService.createABunchOfVertices() == 5
    }
}

PS. I'm using Spring 1.5.10.RELEASE and groovy 2.4.15...


Solution

  • If upgrading to Spock 1.2 is an option for you, I suggest ditching the TestConfig class and using the @SpringBean annotation.

    This is an example of how I have it set up in my tests:

    @ActiveProfiles(["integrationtest"])
    @DirtiesContext
    abstract class IntegrationTest extends Specification {
    
        @SpringBean
        EurekaClient eurekaClient = Mock() {
            getNextServerFromEureka(*_) >> Mock(InstanceInfo) {
                getHomePageUrl() >> "test.test"
            }
        }
    
    //    ... other stuff
    }