Switch mongodb to testcontainers.

See #654
This commit is contained in:
Christoph Strobl
2022-06-02 15:08:11 +02:00
committed by Mark Paluch
parent e2b388e6b9
commit e59e147d6f
9 changed files with 85 additions and 187 deletions

View File

@@ -31,12 +31,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -29,7 +29,5 @@ import org.springframework.data.mongodb.core.mapping.Field;
* @author Christoph Strobl
*/
record Person(@Id ObjectId id, @Field("first_name") String firstname, @Field("last_name") String lastname, int age) {
public Person(String firstname, String lastname, int age) {
this(null, firstname, lastname, age);
}
}

View File

@@ -21,19 +21,19 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.core.query.Update.*;
import example.springdata.mongodb.util.EmbeddedMongo;
import reactor.test.StepVerifier;
import java.time.Duration;
import com.mongodb.client.MongoClient;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.reactivestreams.client.MongoClients;
import example.springdata.mongodb.util.MongoContainers;
import org.assertj.core.api.Assertions;
import org.bson.Document;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -46,11 +46,13 @@ import org.springframework.data.mongodb.core.messaging.ChangeStreamRequest;
import org.springframework.data.mongodb.core.messaging.DefaultMessageListenerContainer;
import org.springframework.data.mongodb.core.messaging.Message;
import org.springframework.data.mongodb.core.messaging.MessageListenerContainer;
import org.springframework.test.context.junit4.SpringRunner;
import com.mongodb.client.MongoClient;
import com.mongodb.client.model.changestream.ChangeStreamDocument;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.test.StepVerifier;
/**
* A simple Test demonstrating required {@link Configuration} for consumption of MongoDB
@@ -60,11 +62,18 @@ import com.mongodb.reactivestreams.client.MongoClients;
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(SpringRunner.class)
@Testcontainers
@ExtendWith(SpringExtension.class)
@DataMongoTest
public class ChangeStreamsTests {
public static @ClassRule EmbeddedMongo replSet = EmbeddedMongo.replSet().configure();
@Container //
private static MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired MessageListenerContainer container; // for imperative style
@@ -72,14 +81,14 @@ public class ChangeStreamsTests {
@Autowired ReactiveMongoOperations reactiveTemplate; // for reactive style
Person gabriel = new Person("Gabriel", "Lorca", 30);
Person michael = new Person("Michael", "Burnham", 30);
Person ash = new Person("Ash", "Tyler", 35);
Person gabriel = new Person(new ObjectId(), "Gabriel", "Lorca", 30);
Person michael = new Person(new ObjectId(), "Michael", "Burnham", 30);
Person ash = new Person(new ObjectId(), "Ash", "Tyler", 35);
/**
* Configuration? Yes we need a bit of it - Do not worry, it won't be much!
*/
@SpringBootApplication(exclude = EmbeddedMongoAutoConfiguration.class)
@SpringBootApplication()
static class Config {
/**
@@ -90,7 +99,7 @@ public class ChangeStreamsTests {
*/
@Bean
MongoClient mongoClient() {
return replSet.getMongoClient();
return com.mongodb.client.MongoClients.create(mongoDBContainer.getReplicaSetUrl());
}
/**
@@ -100,7 +109,7 @@ public class ChangeStreamsTests {
*/
@Bean
SimpleMongoClientDatabaseFactory mongoDbFactory() {
return new SimpleMongoClientDatabaseFactory(replSet.getMongoClient(), "changestreams");
return new SimpleMongoClientDatabaseFactory(mongoClient(), "changestreams");
}
/**
@@ -110,7 +119,7 @@ public class ChangeStreamsTests {
*/
@Bean
SimpleReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory() {
return new SimpleReactiveMongoDatabaseFactory(MongoClients.create(replSet.getConnectionString()),
return new SimpleReactiveMongoDatabaseFactory(MongoClients.create(mongoDBContainer.getReplicaSetUrl()),
"changestreams");
}
@@ -118,7 +127,7 @@ public class ChangeStreamsTests {
* Since listening to a <a href="https://docs.mongodb.com/manual/changeStreams/">Change Stream</a> using the sync
* MongoDB Java Driver is a blocking class, we need to move load to another {@link Thread} by simply using a
* {@link MessageListenerContainer}.
* <p />
* <p/>
* As this is a {@link org.springframework.context.SmartLifecycle smart lifecycle component} we do actually not need
* to worry about its lifecycle, the resource allocation and freeing.
*

View File

@@ -31,12 +31,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -49,14 +49,10 @@ public class ReactiveTransitionService {
public Mono<Integer> run(Integer id) {
return template.inTransaction().execute(action -> {
return lookup(id) //
.flatMap(process -> start(action, process)) //
.flatMap(process -> start(template, process)) //
.flatMap(it -> verify(it)) //
.flatMap(process -> finish(action, process));
}).next().map(Process::id);
.flatMap(process -> finish(template, process)).map(Process::id);
}
private Mono<Process> finish(ReactiveMongoOperations operations, Process process) {

View File

@@ -15,19 +15,21 @@
*/
package example.springdata.mongodb.imperative;
import example.springdata.mongodb.Process;
import example.springdata.mongodb.State;
import example.springdata.mongodb.util.EmbeddedMongo;
import java.util.function.Consumer;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import example.springdata.mongodb.Process;
import example.springdata.mongodb.State;
import example.springdata.mongodb.util.MongoContainers;
import org.assertj.core.api.Assertions;
import org.bson.Document;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -35,14 +37,14 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.mongodb.client.MongoClient;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
/**
* Test showing MongoDB Transaction usage through a synchronous (imperative) API using Spring's managed transactions.
@@ -51,11 +53,17 @@ import com.mongodb.client.model.Projections;
* @currentRead The Core - Peter V. Brett
* @see org.springframework.transaction.annotation.Transactional
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@Testcontainers
@ExtendWith(SpringExtension.class)
public class TransitionServiceTests {
public static @ClassRule EmbeddedMongo replSet = EmbeddedMongo.replSet().configure();
@Container //
private static MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
static final String DB_NAME = "spring-data-tx-examples";
@@ -73,10 +81,9 @@ public class TransitionServiceTests {
return new MongoTransactionManager(dbFactory);
}
@Override
@Bean
public MongoClient mongoClient() {
return replSet.getMongoClient();
return MongoClients.create(mongoDBContainer.getReplicaSetUrl());
}
@Override
@@ -110,5 +117,4 @@ public class TransitionServiceTests {
return State.valueOf(client.getDatabase(DB_NAME).getCollection("processes").find(Filters.eq("_id", process.id()))
.projection(Projections.include("state")).first().get("state", String.class));
}
}

View File

@@ -17,16 +17,14 @@ package example.springdata.mongodb.reactive;
import static org.assertj.core.api.Assertions.*;
import com.mongodb.reactivestreams.client.MongoClients;
import example.springdata.mongodb.Process;
import com.mongodb.reactivestreams.client.MongoClient;
import example.springdata.mongodb.State;
import example.springdata.mongodb.util.EmbeddedMongo;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import example.springdata.mongodb.util.MongoContainers;
import org.bson.Document;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -35,13 +33,16 @@ import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.ReactiveMongoTransactionManager;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
/**
* Test showing MongoDB Transaction usage through a reactive API.
@@ -49,11 +50,17 @@ import com.mongodb.reactivestreams.client.MongoClients;
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
@Testcontainers
@ExtendWith(SpringExtension.class)
public class ReactiveManagedTransitionServiceTests {
public static @ClassRule EmbeddedMongo replSet = EmbeddedMongo.replSet().configure();
@Container //
private static MongoDBContainer mongoDBContainer = MongoContainers.getDefaultContainer();
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired ReactiveManagedTransitionService managedTransitionService;
@Autowired MongoClient client;
@@ -74,7 +81,7 @@ public class ReactiveManagedTransitionServiceTests {
@Bean
@Override
public MongoClient reactiveMongoClient() {
return MongoClients.create(replSet.getConnectionString());
return MongoClients.create(mongoDBContainer.getReplicaSetUrl());
}
@Override

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2018-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package example.springdata.mongodb.reactive;
import static org.assertj.core.api.Assertions.*;
import example.springdata.mongodb.Process;
import example.springdata.mongodb.State;
import example.springdata.mongodb.util.EmbeddedMongo;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.bson.Document;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
/**
* Test showing MongoDB Transaction usage through a reactive API.
*
* @author Christoph Strobl
* @currentRead The Core - Peter V. Brett
*/
@RunWith(SpringRunner.class)
@ContextConfiguration
public class ReactiveTransitionServiceTests {
public static @ClassRule EmbeddedMongo replSet = EmbeddedMongo.replSet().configure();
@Autowired ReactiveTransitionService transitionService;
@Autowired MongoClient client;
static final String DB_NAME = "spring-data-reactive-tx-examples";
@Configuration
@ComponentScan
@EnableReactiveMongoRepositories
static class Config extends AbstractReactiveMongoConfiguration {
@Bean
@Override
public MongoClient reactiveMongoClient() {
return MongoClients.create(replSet.getConnectionString());
}
@Override
protected String getDatabaseName() {
return DB_NAME;
}
}
@Test
public void reactiveTxCommitRollback() {
for (var i = 0; i < 10; i++) {
transitionService.newProcess() //
.map(Process::id) //
.flatMap(transitionService::run) //
.onErrorReturn(-1).as(StepVerifier::create) //
.consumeNextWith(val -> {}) //
.verifyComplete();
}
Flux.from(client.getDatabase(DB_NAME).getCollection("processes").find(new Document())) //
.buffer(10) //
.as(StepVerifier::create) //
.consumeNextWith(list -> {
for (var document : list) {
System.out.println("document: " + document);
if (document.getInteger("_id") % 3 == 0) {
assertThat(document.getString("state")).isEqualTo(State.CREATED.toString());
} else {
assertThat(document.getString("state")).isEqualTo(State.DONE.toString());
}
}
}) //
.verifyComplete();
}
}

View File

@@ -0,0 +1 @@
spring.data.mongodb.database=spring-data-tx-examples