Skip to content

Commit 50769d0

Browse files
oxcafedeadartembilan
authored andcommitted
GH-3839: Migrate MongoDB tests to Testcontainers
Fixes #3839 * Create a new base interface for Mongo container-based tests * Migrate all tests to JUnit5 * Remove obsolete classes related to JUnit4 Mongo rule * Fix code style & readability here and there, a few Sonar issues * Fix failing test `validateWithConfiguredPollerFlow` * * This test was failing with Mongo error `$and/$or/$nor must be a nonempty array`. The cause was too small polling ratio of the update query, the consecutive read queries failed as no 'Oleg' document was in collection after ~100 ms. Couple of changes after the pull request review: * Get back the blank lines in test classes * Get back the blank lines for some inner classes * Eliminate bad renaming consequences * Remove a couple of garbage todos * Couple of places with code cleanup
1 parent 8506716 commit 50769d0

File tree

36 files changed

+930
-1062
lines changed

36 files changed

+930
-1062
lines changed
Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2020-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,14 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.integration.mongodb.rules;
17+
package org.springframework.integration.mongodb;
1818

1919
import java.time.Duration;
2020

2121
import org.bson.Document;
2222
import org.bson.UuidRepresentation;
2323
import org.bson.conversions.Bson;
24-
import org.junit.Rule;
24+
import org.junit.jupiter.api.BeforeAll;
25+
import org.testcontainers.containers.GenericContainer;
26+
import org.testcontainers.junit.jupiter.Testcontainers;
2527

2628
import org.springframework.dao.DataAccessException;
2729
import org.springframework.data.annotation.Id;
@@ -40,50 +42,64 @@
4042
import org.springframework.integration.mongodb.outbound.MessageCollectionCallback;
4143
import org.springframework.messaging.Message;
4244

45+
import com.mongodb.ConnectionString;
4346
import com.mongodb.MongoClientSettings;
4447
import com.mongodb.MongoException;
4548
import com.mongodb.client.MongoClients;
4649
import com.mongodb.client.MongoCollection;
4750

48-
4951
/**
50-
* Convenience base class that enables unit test methods to rely upon the {@link MongoDbAvailable} annotation.
52+
* The base contract for all tests requiring a MongoDb connection.
53+
* The Testcontainers 'reuse' option must be disabled, so, Ryuk container is started
54+
* and will clean all the containers up from this test suite after JVM exit.
55+
* Since the Redis container instance is shared via static property, it is going to be
56+
* started only once per JVM, therefore the target Docker container is reused automatically.
5157
*
5258
* @author Oleg Zhurakousky
5359
* @author Xavier Padro
5460
* @author Artem Bilan
5561
* @author David Turanski
62+
* @author Artem Vozhdayenko
5663
*
57-
* @since 2.1
64+
* @since 6.0
5865
*/
59-
public abstract class MongoDbAvailableTests {
60-
61-
@Rule
62-
public MongoDbAvailableRule mongoDbAvailableRule = new MongoDbAvailableRule();
63-
64-
public static final MongoDatabaseFactory MONGO_DATABASE_FACTORY =
65-
new SimpleMongoClientDatabaseFactory(
66-
MongoClients.create(
67-
MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD).build()),
68-
"test");
69-
70-
public static final ReactiveMongoDatabaseFactory REACTIVE_MONGO_DATABASE_FACTORY =
71-
new SimpleReactiveMongoDatabaseFactory(
72-
com.mongodb.reactivestreams.client.MongoClients.create(
73-
MongoClientSettings.builder().uuidRepresentation(UuidRepresentation.STANDARD).build()),
74-
"test");
75-
76-
protected MongoDatabaseFactory prepareMongoFactory(String... additionalCollectionsToDrop) {
77-
cleanupCollections(MONGO_DATABASE_FACTORY, additionalCollectionsToDrop);
78-
return MONGO_DATABASE_FACTORY;
66+
@Testcontainers(disabledWithoutDocker = true)
67+
public interface MongoDbContainerTest {
68+
69+
GenericContainer<?> MONGO_CONTAINER = new GenericContainer<>("mongo:5.0.9")
70+
.withExposedPorts(27017);
71+
72+
@BeforeAll
73+
static void startContainer() {
74+
MONGO_CONTAINER.start();
75+
}
76+
77+
static MongoDatabaseFactory createMongoDbFactory() {
78+
return new SimpleMongoClientDatabaseFactory(MongoClients.create(getMongoClientSettings()), "test");
79+
}
80+
81+
static ReactiveMongoDatabaseFactory createReactiveMongoDbFactory() {
82+
return new SimpleReactiveMongoDatabaseFactory(
83+
com.mongodb.reactivestreams.client.MongoClients.create(getMongoClientSettings()),
84+
"test");
85+
}
86+
87+
private static MongoClientSettings getMongoClientSettings() {
88+
return MongoClientSettings.builder()
89+
.applyConnectionString(new ConnectionString(
90+
"mongodb://localhost:" + MONGO_CONTAINER.getFirstMappedPort()))
91+
.uuidRepresentation(UuidRepresentation.STANDARD).build();
92+
}
93+
94+
static void prepareMongoData(MongoDatabaseFactory mongoDatabaseFactory, String... additionalCollectionsToDrop) {
95+
cleanupCollections(mongoDatabaseFactory, additionalCollectionsToDrop);
7996
}
8097

81-
protected ReactiveMongoDatabaseFactory prepareReactiveMongoFactory(String... additionalCollectionsToDrop) {
82-
cleanupCollections(REACTIVE_MONGO_DATABASE_FACTORY, additionalCollectionsToDrop);
83-
return REACTIVE_MONGO_DATABASE_FACTORY;
98+
static void prepareReactiveMongoData(ReactiveMongoDatabaseFactory mongoDatabaseFactory, String... additionalCollectionsToDrop) {
99+
cleanupCollections(mongoDatabaseFactory, additionalCollectionsToDrop);
84100
}
85101

86-
protected void cleanupCollections(ReactiveMongoDatabaseFactory mongoDbFactory,
102+
static void cleanupCollections(ReactiveMongoDatabaseFactory mongoDbFactory,
87103
String... additionalCollectionsToDrop) {
88104

89105
ReactiveMongoTemplate template = new ReactiveMongoTemplate(mongoDbFactory);
@@ -95,7 +111,7 @@ protected void cleanupCollections(ReactiveMongoDatabaseFactory mongoDbFactory,
95111
}
96112
}
97113

98-
protected void cleanupCollections(MongoDatabaseFactory mongoDbFactory, String... additionalCollectionsToDrop) {
114+
static void cleanupCollections(MongoDatabaseFactory mongoDbFactory, String... additionalCollectionsToDrop) {
99115
MongoTemplate template = new MongoTemplate(mongoDbFactory);
100116
template.dropCollection("messages");
101117
template.dropCollection("configurableStoreMessages");
@@ -105,7 +121,7 @@ protected void cleanupCollections(MongoDatabaseFactory mongoDbFactory, String...
105121
}
106122
}
107123

108-
protected Person createPerson() {
124+
static Person createPerson() {
109125
Address address = new Address();
110126
address.setCity("Philadelphia");
111127
address.setStreet("2121 Rawn street");
@@ -117,7 +133,7 @@ protected Person createPerson() {
117133
return person;
118134
}
119135

120-
protected Person createPerson(String name) {
136+
static Person createPerson(String name) {
121137
Address address = new Address();
122138
address.setCity("Philadelphia");
123139
address.setStreet("2121 Rawn street");
@@ -129,7 +145,7 @@ protected Person createPerson(String name) {
129145
return person;
130146
}
131147

132-
public static class Person {
148+
class Person {
133149

134150
@Id
135151
private String id;
@@ -156,7 +172,7 @@ public void setName(String name) {
156172

157173
}
158174

159-
public static class Address {
175+
class Address {
160176

161177
private String street;
162178

@@ -190,7 +206,7 @@ public void setState(String state) {
190206

191207
}
192208

193-
public static class TestMongoConverter extends MappingMongoConverter {
209+
class TestMongoConverter extends MappingMongoConverter {
194210

195211
public TestMongoConverter(
196212
MongoDatabaseFactory mongoDbFactory,
@@ -211,7 +227,7 @@ public <S> S read(Class<S> clazz, Bson source) {
211227

212228
}
213229

214-
public static class ReactiveTestMongoConverter extends MappingMongoConverter {
230+
class ReactiveTestMongoConverter extends MappingMongoConverter {
215231

216232
public ReactiveTestMongoConverter(
217233
ReactiveMongoDatabaseFactory mongoDbFactory,
@@ -232,7 +248,7 @@ public <S> S read(Class<S> clazz, Bson source) {
232248

233249
}
234250

235-
public static class TestCollectionCallback implements MessageCollectionCallback<Long> {
251+
class TestCollectionCallback implements MessageCollectionCallback<Long> {
236252

237253
@Override
238254
public Long doInCollection(MongoCollection<Document> collection, Message<?> message)

spring-integration-mongodb/src/test/java/org/springframework/integration/mongodb/config/MongoDbInboundChannelAdapterIntegrationTests-context.xml

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,39 @@
33
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
44
xmlns:int="http://www.springframework.org/schema/integration"
55
xmlns:int-mongodb="http://www.springframework.org/schema/integration/mongodb"
6-
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
76
xmlns:tx="http://www.springframework.org/schema/tx"
8-
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
9-
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
7+
xsi:schemaLocation="http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
108
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
119
http://www.springframework.org/schema/integration/mongodb https://www.springframework.org/schema/integration/mongodb/spring-integration-mongodb.xsd
1210
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
1311

14-
<mongo:db-factory id="mongoDbFactory" dbname="test" />
12+
<bean id="mongoDbFactory" class="org.springframework.integration.mongodb.MongoDbContainerTest"
13+
factory-method="createMongoDbFactory"/>
1514

1615
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapter"
1716
channel="replyChannel"
1817
query="{'name' : 'Bob'}"
1918
entity-class="java.lang.Object"
2019
auto-startup="false">
21-
<int:poller fixed-delay="100" />
20+
<int:poller fixed-delay="100"/>
2221
</int-mongodb:inbound-channel-adapter>
2322

2423
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterNamedFactory"
2524
mongodb-factory="mongoDbFactory"
2625
channel="replyChannel"
2726
query="{'name' : 'Bob'}"
2827
auto-startup="false">
29-
<int:poller fixed-delay="5000" />
28+
<int:poller fixed-delay="5000"/>
3029
</int-mongodb:inbound-channel-adapter>
3130

3231
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithTemplate"
3332
channel="replyChannel"
3433
mongo-template="mongoDbTemplate"
3534
query="{'name' : 'Bob'}"
3635
expect-single-result="true"
37-
entity-class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.Person"
36+
entity-class="org.springframework.integration.mongodb.MongoDbContainerTest.Person"
3837
auto-startup="false">
39-
<int:poller fixed-delay="5000" />
38+
<int:poller fixed-delay="5000"/>
4039
</int-mongodb:inbound-channel-adapter>
4140

4241
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithNamedCollection"
@@ -46,7 +45,7 @@
4645
query="{'name' : 'Bob'}"
4746
entity-class="java.lang.Object"
4847
auto-startup="false">
49-
<int:poller fixed-delay="5000" />
48+
<int:poller fixed-delay="5000"/>
5049
</int-mongodb:inbound-channel-adapter>
5150
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithStringQueryExpression"
5251
channel="replyChannel"
@@ -55,7 +54,7 @@
5554
query-expression="new String('{''name'' : ''Bob''}')"
5655
entity-class="java.lang.Object"
5756
auto-startup="false">
58-
<int:poller fixed-rate="5000" />
57+
<int:poller fixed-rate="5000"/>
5958
</int-mongodb:inbound-channel-adapter>
6059

6160
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithQueryExpression"
@@ -65,7 +64,7 @@
6564
query-expression="new BasicQuery('{''name'' : ''Bob''}').limit(1)"
6665
entity-class="java.lang.Object"
6766
auto-startup="false">
68-
<int:poller fixed-rate="5000" />
67+
<int:poller fixed-rate="5000"/>
6968
</int-mongodb:inbound-channel-adapter>
7069

7170
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithNamedCollectionExpression"
@@ -75,7 +74,7 @@
7574
query="{'name' : 'Bob'}"
7675
entity-class="java.lang.Object"
7776
auto-startup="false">
78-
<int:poller fixed-delay="5000" />
77+
<int:poller fixed-delay="5000"/>
7978
</int-mongodb:inbound-channel-adapter>
8079

8180
<int-mongodb:inbound-channel-adapter id="inboundAdapterWithOnSuccessDisposition"
@@ -84,12 +83,12 @@
8483
auto-startup="false">
8584

8685
<int:poller fixed-delay="200" max-messages-per-poll="1">
87-
<int:advice-chain synchronization-factory="syncFactory">
86+
<int:advice-chain synchronization-factory="syncFactory">
8887
<bean
89-
class="org.springframework.integration.mongodb.config.MongoDbInboundChannelAdapterIntegrationTests.TestMessageSourceAdvice" />
88+
class="org.springframework.integration.mongodb.config.MongoDbInboundChannelAdapterIntegrationTests.TestMessageSourceAdvice"/>
9089
<tx:advice>
9190
<tx:attributes>
92-
<tx:method name="*" />
91+
<tx:method name="*"/>
9392
</tx:attributes>
9493
</tx:advice>
9594
</int:advice-chain>
@@ -102,7 +101,7 @@
102101
</int:transaction-synchronization-factory>
103102

104103
<int:channel id="afterCommitChannel">
105-
<int:queue />
104+
<int:queue/>
106105
</int:channel>
107106

108107
<int-mongodb:inbound-channel-adapter id="mongoInboundAdapterWithConverter"
@@ -111,28 +110,28 @@
111110
entity-class="java.lang.Object"
112111
mongo-converter="mongoConverter"
113112
auto-startup="false">
114-
<int:poller fixed-delay="100" />
113+
<int:poller fixed-delay="100"/>
115114
</int-mongodb:inbound-channel-adapter>
116115

117116
<bean id="documentCleaner"
118-
class="org.springframework.integration.mongodb.config.MongoDbInboundChannelAdapterIntegrationTests.DocumentCleaner" />
117+
class="org.springframework.integration.mongodb.config.MongoDbInboundChannelAdapterIntegrationTests.DocumentCleaner"/>
119118

120119
<bean id="mongoDbTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
121-
<constructor-arg ref="mongoDbFactory" />
120+
<constructor-arg ref="mongoDbFactory"/>
122121
</bean>
123122

124123
<bean id="mongoConverter"
125-
class="org.springframework.integration.mongodb.rules.MongoDbAvailableTests.TestMongoConverter">
126-
<constructor-arg ref="mongoDbFactory" />
124+
class="org.springframework.integration.mongodb.MongoDbContainerTest.TestMongoConverter">
125+
<constructor-arg ref="mongoDbFactory"/>
127126
<constructor-arg>
128-
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
127+
<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext"/>
129128
</constructor-arg>
130129
</bean>
131130

132131
<int:channel id="replyChannel">
133-
<int:queue />
132+
<int:queue/>
134133
</int:channel>
135134

136-
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager" />
135+
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
137136

138137
</beans>

0 commit comments

Comments
 (0)