Stefan Helmerichs
Stefan Helmerichs

Reputation: 502

Unit / Integration Testing with Spring Data & MongoDB is not able to mock Repositories

Some small infos beforehand:

Dependencies

Spring Boot 1.3.0
Spring Mongo 1.3.3
Spring Security 3.1.4
Spring Security Cas 4.0.2
Flapdoodle Embedmongo 1.46.0

Base

Right now, we have an abstract test class annotated with

@RunWith(SpringJUnit4ClassRunner.class), @SpringApplicationConfiguration(TestConfig.class) and @WebAppConfiguration.

The TestConfig-class looks like this:

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@ComponentScan(value = { "package1", "package2" })
public class TestConfig {

}

As you see, it scans everything in those 2 packages, and fetches several other configuration classes as well:

@Configuration
// needed if working with Spring-Data-Repository interfaces and subprojects
// http://stackoverflow.com/questions/29084824/spring-repository-components-not-found-in-gradle-subproject-springboot
@EnableMongoRepositories({ "package2.subpackage" })
public class ModelConfiguration {

}

and

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
@EnableMongoRepositories(basePackages = { "package2" })
public class TestMongoConfiguration {

    private static final String DESTROY_METHOD_CLOSE = "close";
    private static final String DESTROY_METHOD_STOP = "stop";

    private static final MongodStarter STARTER = MongodStarter.getDefaultInstance();

    @Autowired
    private MongoProperties mongoProperties;

    @Autowired(required = false)
    private MongoClientOptions mongoClientOptions;

    @Autowired
    private Environment environment;

    @Bean(destroyMethod = DESTROY_METHOD_CLOSE)
    public MongoClient mongo() throws IOException {
        Net net = mongodProcess().getConfig().net();
        mongoProperties.setHost(net.getServerAddress().getHostName());
        mongoProperties.setPort(net.getPort());
        return mongoProperties.createMongoClient(this.mongoClientOptions, environment);
    }

    @Bean(destroyMethod = DESTROY_METHOD_STOP)
    public MongodProcess mongodProcess() throws IOException {
        return mongodExecutable().start();
    }

    @Bean(destroyMethod = DESTROY_METHOD_STOP)
    public MongodExecutable mongodExecutable() throws IOException {
        return STARTER.prepare(mongodConfig());
    }

    @Bean
    public IMongodConfig mongodConfig() throws IOException {
        return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
    }
}

And this is part of my test class:

@SpringApplicationConfiguration(classes = TestClass.TestConfiguration.class)
public class TestClass extends AbstractTest {

    @Order(Ordered.HIGHEST_PRECEDENCE)
    public static class TestConfiguration {
        @Bean
        @Primary
        public FooRepository fooRepository() {
            FooRepository mock = mock(FooRepository.class);
            // mockbehaviour
            return mock;
        }

        @Bean
        @Primary
        public FooDao fooDao() {
            FooDao mock = mock(FooDao.class);
            //Mock behaviour
            return mock;
        }

        @Bean
        @Primary
        public BarDao barDao() {
            BarDao mock = mock(BarDao.class);
            //MockBehaviour
            return mock;
        }

        @Bean
        @Primary
        public BarRepository barRepository() {
            BarRepository mock = mock(BarRepository.class);
            return mock;
        }
    }

I am creating several requests now which are passed to the superclass for security testing, and (since all tests have only one successful case anyway) I then try to verify the corresponding calls to the Daos and Repositories via verify(mock).

The Beans are injected to the Controller by @Resource, the same way I am using them in the test class.

Problem

For some reason, the Daos are working as intended (are created, behaviour is specified, are called and can be verified), the Repositories are not - the regular proxies are created and used. Before using @Order(Ordered.HIGHEST_PRECEDENCE), the bean creation was simply skipped (and logged as such) since the top-level bean already registered denied being overwritten.

After resorting to this, the Repository-beans have been overridden instead of skipped (and, of course, been logged as such). The @Primary at bean level was supposed to make sure the mocks are used instead of the actual beans created by the component scan - and on other test instances, which do not start up the entire application and the embedded Mongo, this works as intended - but here, it does not.

Things I have tried

I have been looking for several days now, always and the only possible solutions I have stumbled upon are: create an xml-configuration (which is a no-go, since we are supposed to create everything without the need for xml-files), use Fongo (which would require to import a new dependency for this one step, as the Database-tests are happening somewhere else entirely, and then removing it), or disable the bean overriding (which I have absolutely no idea on doing and the Spring docs are not really helpful - yes, the method is there, but whenever I try to do this, it does not affect the current context).

Upvotes: 3

Views: 5860

Answers (1)

Danilo Tommasina
Danilo Tommasina

Reputation: 1760

Sorry this would fit more in a comment rather than an answer, but the formatting is too bad...

No idea why it doesn't work, however did you try to using explicit import of @Configuration beans versus usage of ComponenScan?

e.g.:

@SpringApplicationConfiguration(classes = TestClass.TestConfiguration.class)
public class TestClass extends AbstractTest {

    @Configuration
    @Import( TestConfig.class )
    public static class TestConfiguration {
       //...
    }
}

@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
// @ComponentScan(value = { "package1", "package2" }) // GET RID OF THIS
@Import( { TestMongoConfiguration.class /*, OtherConfig.class, ... */ } )
public class TestConfig {

}

This way you can control the order on how the configurations get loaded.

Upvotes: 3

Related Questions