当前位置: 首页 > 知识库问答 >
问题:

在Spring Boot中用Flapdoodle嵌入MongoDB测试@Transactional

颜畅
2023-03-14

我想确保@Transactional注释工作,所以我写了一篇测试save and Publishing文章--我的kafka publisher是一个模拟,它会在任何调用上抛出异常。我想确保MongoDB回滚持久化的文章。

@Test
void testRollbackOnPublishFail() {
    when(producer.publishArticle(any())).thenThrow(IllegalStateException.class);
    ArticleDocument articleDocument = ArticleTestDataUtil.createArticleDocument();
    try {
        ArticleDocument publishedDocument = articleService.saveAndPublish(articleDocument);
    } catch (Exception e) {
        assertTrue(e instanceof IllegalStateException);
    }
    assertFalse(articleService.findById(articleDocument.getId()).isPresent());
}
 testCompile "de.flapdoodle.embed:de.flapdoodle.embed.mongo:2.2.0"
@Configuration
public class MongoTransactionConfig {

    @Bean
    public MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
        return new MongoTransactionManager(dbFactory);
    }
}

现在我的测试失败了,因为无法在MongoClient中启动会话

com.mongodb.MongoClientException: Sessions are not supported by the MongoDB cluster to which this client is connected
        at com.mongodb.MongoClient.startSession(MongoClient.java:560)

我还尝试创建一个自定义的IMongodConfig

@Bean(name = "customReplicaMongodConfig")
    public IMongodConfig mongodConfig(EmbeddedMongoProperties embeddedProperties) throws IOException {
        Storage storage = new Storage("/tmp", "rs0", 0);
        return new MongodConfigBuilder()
                .shardServer(true)
                .version(Version.V4_0_2)
                .net(new Net(27117, Network.localhostIsIPv6()))
                .replication(storage)
                .cmdOptions(new MongoCmdOptionsBuilder().useNoJournal(false).build()).build();
    }

并启动复制:

@ConditionalOnBean(name = "customReplicaMongodConfig")
@Configuration
public class ReplicaConfig {

    @Inject
    private MongoClient mongoClient;

    @PostConstruct
    public void initiateReplicationSet() {
        mongoClient.getDatabase("admin").runCommand(new Document("replSetInitiate", new Document()));
    }
}

因此,我的问题是,是否有可能创建一个运行的复制集与嵌入MongoDB测试事务性。

共有1个答案

赵俊远
2023-03-14

您可以在这里找到有关创建副本集的信息

我的科特林解决方案:

import com.mongodb.BasicDBList
import com.mongodb.BasicDBObjectBuilder
import com.mongodb.DBObject
import com.mongodb.client.MongoClient
import com.mongodb.client.MongoClients
import com.mongodb.client.MongoCollection
import com.mongodb.client.MongoDatabase
import de.flapdoodle.embed.mongo.MongodExecutable
import de.flapdoodle.embed.mongo.MongodProcess
import de.flapdoodle.embed.mongo.MongodStarter
import de.flapdoodle.embed.mongo.config.MongoCmdOptionsBuilder
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder
import de.flapdoodle.embed.mongo.config.Net
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.process.runtime.Network
import org.assertj.core.api.Assertions.assertThat
import org.bson.Document
import org.junit.jupiter.api.Test
import org.springframework.data.mongodb.MongoDatabaseFactory
import org.springframework.data.mongodb.MongoTransactionManager
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory
import org.springframework.test.context.ActiveProfiles
import org.springframework.transaction.TransactionStatus
import org.springframework.transaction.support.TransactionCallbackWithoutResult
import org.springframework.transaction.support.TransactionTemplate
import java.io.IOException


@ActiveProfiles("test")

class EmbeddedMongoDbTransactionTest {
    private val CONNECTION_STRING = "mongodb://%s:%d/"  

    private var node1MongodExe: MongodExecutable? = null
    private var node1Mongod: MongodProcess? = null
    private var mongo: MongoClient? = null
    private var node2MongodExe: MongodExecutable? = null
    private var node2Mongod: MongodProcess? = null

    @Test
    @Throws(IOException::class)
    fun testSmth() {
        val runtime = MongodStarter.getDefaultInstance()
        val node1Port = 57023
        val node2Port = 57024
        try {
            node1MongodExe = runtime.prepare(
                MongodConfigBuilder().version(Version.Main.PRODUCTION)
                    .withLaunchArgument("--replSet", "rs0")
                    .cmdOptions(MongoCmdOptionsBuilder().useNoJournal(false).build())
                    .net(Net(node1Port, Network.localhostIsIPv6())).build()
            )
            node1Mongod = node1MongodExe?.start()
            node2MongodExe = runtime.prepare(
                MongodConfigBuilder().version(Version.Main.PRODUCTION)
                    .withLaunchArgument("--replSet", "rs0")
                    .cmdOptions(MongoCmdOptionsBuilder().useNoJournal(false).build())
                    .net(Net(node2Port, Network.localhostIsIPv6())).build()
            )
            node2Mongod = node2MongodExe?.start()
            mongo = MongoClients.create(CONNECTION_STRING.format("localhost", node1Port))
            val adminDatabase: MongoDatabase = mongo!!.getDatabase("admin")

            val config = Document("_id", "rs0")
            val members = BasicDBList()
            members.add(Document("_id", 0).append("host", "localhost:$node1Port"))
            members.add(Document("_id", 1).append("host", "localhost:$node2Port"))
            config.put("members", members)

            adminDatabase.runCommand(Document("replSetInitiate", config))

            println(">>>>>> wait")
            println(">>>>>>>>" + adminDatabase.runCommand(Document("replSetGetStatus", 1)))
            Thread.sleep(15_000) // without waiting fails with error : 'not master' on server

            val funDb: MongoDatabase = mongo?.getDatabase("fun")!!

            // insert test 1
            val testCollection: MongoCollection<Document> = funDb.getCollection("test")
            println(">>>>>>>> inserting data")
            testCollection.insertOne(Document("fancy", "value"))
            println(">>>>>>>> finding data")
            assertThat(testCollection.find().first()!!.get("fancy")).isEqualTo("value")


            // insert test 2 (with transaction)
            val mongoTemplate = MongoTemplate(mongo!!, "test")

            // Without creating collection in advance fails with error:
            // Cannot create namespace in multi-document transaction
            // (https://stackoverflow.com/questions/52585715/cannot-create-namespace-in-multi-document-transactionmongodb-4-0-spring-data-2)
            mongoTemplate.createCollection("collection")

            val mongoDatabaseFactory: MongoDatabaseFactory = SimpleMongoClientDatabaseFactory(mongo!!, "test")
            val mongoTransactionManager = MongoTransactionManager(mongoDatabaseFactory)

            val transactionTemplate = TransactionTemplate(mongoTransactionManager)

            transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
                override fun doInTransactionWithoutResult(status: TransactionStatus) {
                    val objectToSave = BasicDBObjectBuilder.start()
                        .add("key", "value")
                        .get()

                    // when
                    mongoTemplate.save(objectToSave, "collection")

                    // then
                    assertThat(mongoTemplate.findAll(DBObject::class.java, "collection"))
                        .extracting("key")
                        .containsOnly("value")
                }
            })

            // after transaction
            assertThat(mongoTemplate.findAll(DBObject::class.java, "collection"))
                .extracting("key")
                .containsOnly("value")

        } finally {
            println(">>>>>> shutting down")
            mongo?.close()
            node1MongodExe?.stop()
            node1Mongod?.stop()
            node2MongodExe?.stop()
            node2Mongod?.stop()
        }
    }
}
 类似资料:
  • 我在这里读了很多与这个配置相关的问题,但没有一个与我的问题相关。例如如何使用spring boot和spring data配置两个mongodb实例 flapdoodle配置对此有一个实现,但我不确定如何访问它。 https://github.com/flapdoodle-oss/de.flapdoodle.embedd.mongo/blob/master/src/main/java/de/fla

  • 我有一个推送到GitHub的SpringBoot项目,其中我有一些半集成测试,其中我使用嵌入式mongo作为数据库。我的构建在本地是成功的,测试正在通过,但是在运行“MavenJava”GitHub操作时,它会失败,原因如下:

  • 问题内容: 我想测试嵌入式设备的功能。为简化起见,我可以说这是一个人形机器人,由PC通过C / C ++ API进行远程控制。 我非常有兴趣使用它,因为它没有样板方法。但是,我的情况要复杂一些。实际测试在C#程序上运行,大约需要24小时才能完成。通过切换到Python,我可能会节省大量开发新测试的时间。但是,在这样做之前,我正在寻找一些答案。 古老的测试套件的第一个问题是,所有测试都以预定的顺序执

  • 与@mockbean和@spybean一样,有没有类似于@fakebean/@dummybean的东西? 其思想是,该实例是100%真实的(具有预期的生产内部状态),并且它覆盖(或者添加bean,以防在配置中没有声明)上下文中的bean。理想情况下,您不需要创建TestConfiguration类并将其设置为Primary,因为这样可以在每个测试的基础上控制假冒,只有在需要时才可以。否则它使用主的

  • 我在GET api中有多个查询参数(如姓名、年龄、性别、位置等…n个数字)。现在我需要使用这些查询值来查询我的mongo数据库。现在用户可以发送从0到n的查询参数。 我正在尝试使用类似的东西 或者 但问题是,考虑到用户可以发送的所有排列和组合,我将不得不编写多个查询。有没有更好的方法来做到这一点?

  • 我正在使用以下依赖项: 创建了新的测试类: 我在Spring Boot中创建了测试用例,但是我得到了这个错误: 这是我的应用程序类: 知道我为什么不能在测试类中注入bean吗? 我按照建议删除了@ContextConfiguration,@ComponentScan,@ConnecationTes现在我看到了不同的异常: