问题
I'm currently trying to write an Integration test class which uses Spring Data Mongo repositories.
I use an embedded Mongo instance provided by the de.flapdoodle.embed.mongo
dependency. Spring Data documentation specifies that we only have to put this dependency in the project and the EmbedMongoAutoConfiguration
takes care of the rest.
For now, that's ok, and setting the port to 0
makes the auto configuration process to find a free port to launch the mongo instance on.
This feature is necessary for me to avoid collision with other tests (which are run on a Jenkins CI server along with other project of my company).
The problem comes now, I want to be able to inject some test data from some external file before each of my test method run. I found out that NoSQL Unit can do this with a simple method annotation and a JUnit @Rule
.
Here is an example:
@Value("${local.mongo.port}")
private int mongoPort; // <- still 0 a the time the Rule below is created.
@Rule
public MongoDbRule managedMongoDb = new MongoDbRule(MongoDbConfigurationBuilder.mongoDb().databaseName("myAwesomeDb").port(mongoPort).build());
@Test
@UsingDataSet(locations = "testdata.json", loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
public void testMyData() {
// ...
}
My problem is that, the @Rule
needs the Mongo port in its builder to instantiate the underlying MongoClient, but at the time the @Rule is instantiated, the Spring context is not fully initialized and the EmbeddedMongoAutoConfiguration
has not published the port yet.
So my question is, is there anyone who has ever used the Embedded Mongo feature with NoSQL Unit, and is there any way to, for example create the @Rule
after the Spring context is initialized ?
I was wondering if finding the free port myself (in a static way), setting it to the @Rule
and then tell the EmbeddedMongoAutoConfiguration
to use it by overriding the IMongodConfig
bean was a good idea ? or is there a "simpler" way ?
Note: I just saw that flapdoodle library provides a class and a static method to find a free server port and its used by Spring like this:
Network.getFreeServerPort(getHost()), Network.localhostIsIPv6()))
Thanks everyone in advance!
EDIT: I tried the solution I talked just above, and it seems to work, though I still think it's a bit "verbose" and dirty.
private static final Logger log = LoggerFactory.getLogger(MyAwesomeIT.class);
private static int mongoPort;
static {
try {
mongoPort = Network.getFreeServerPort();
} catch (IOException e) {
log.error("Error while trying to find a free port for Mongo", e);
mongoPort = -1; // test should then not work
}
}
@Rule
public MongoDbRule managedMongoDb = new MongoDbRule(MongoDbConfigurationBuilder.mongoDb().databaseName("myAwesomeDb").port(mongoPort).build());
then in the associated configuration class :
@Configuration
@EnableAutoConfiguration
@EnableMongoRepositories
@EnableConfigurationProperties(MongoProperties.class)
static class ContextConfiguration {
@Autowired
private MongoProperties mongoProperties;
@PostConstruct
public void init() {
// Here, I override the port property
mongoProperties.setPort(mongoPort);
}
}
回答1:
Refining the solution given by @user6599111, it is possible to obtain the port
randomly chosen by Flapdoodle Embedded Mongo, simply injecting an object of type IMongodConfig
.
Spring Boot builds automagically this object for you, as stated here.
Then, the configuration class will become the following.
@Configuration
@EnableAutoConfiguration(exclude = { EmbeddedMongoAutoConfiguration.class })
public class MongoConfiguration {
@Autowired
private Environment environment;
@Autowired
private MongoProperties properties;
@Autowired(required = false)
private MongoClientOptions options;
@Autowired
private IMongodConfig mongoConfig;
@Bean
public MongoClient mongo() throws Exception {
properties.setPort(mongoConfig.net().getPort());
return properties.createMongoClient(this.options, this.environment);
}
}
回答2:
I had the same problem and this was my solution
@Configuration public class EmbeddedMongoConfig extends AbstractMongoConfiguration { @Autowired private Environment environment; @Autowired private MongoProperties properties; @Autowired(required = false) private MongoClientOptions options; @Override protected String getDatabaseName() { return properties.getDatabase(); } @Override @Bean(destroyMethod = "close") public Mongo mongo() throws Exception { properties.setPort(Network.getFreeServerPort()); return properties.createMongoClient(this.options, this.environment); } }
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = { AppRunner.class, EmbeddedMongoConfig.class }) public class BaseTest { }
public class CategoryServiceTest extends BaseTest{ @Autowired private CategoryService categoryService; @Test public void someTest(){ fail("Test not implemented"); } }
回答3:
I have tried this:
int mongoPort = SocketUtils.findAvailableTcpPort();
Source: https://docs.spring.io/spring/docs/4.0.5.RELEASE/javadoc-api/org/springframework/util/SocketUtils.html
This worked for me.
We had embedded Mongo running for unit tests and where there were multiple applications building on Jenkins, some of them failed since the port was same for everyone. Manually changing the ports was also tried but since there were many applications and some of them used a common base class, it was failing.
I am not sure about the @Rule part but you can try this.
来源:https://stackoverflow.com/questions/36176132/use-spring-data-random-embedded-mongo-port-with-nosql-junit-rule