使用托管Mongodb进行Grails集成测试

时间:2014-05-27 17:34:02

标签: mongodb grails intellij-idea gradle integration-testing

我目前正在使用mongoDB,我希望可以在任何机器上运行集成和功能测试(目前在专用构建服务器和未来的CI服务器中)。

主要问题是我必须能够检查mongodb安装(如果不存在,安装它),在启动时启动mongodb实例并在进程完成后将其关闭。

这里有一个已经开发的问题Embedded MongoDB when running integration tests建议安装gradle或maven插件。

这个gradle插件https://github.com/sourcemuse/GradleMongoPlugin/可以执行此操作,但我必须使用它来管理我的依赖项,已经尝试过了。这种方法的问题本身并不是问题,但是当我尝试这个时,我已经失去了IDE(STS,intellij)的所有好处。

有没有人设法做到这一点?

如果某人配置grails与grails项目同时失去了grails的观点,我也会感谢你的帮助!

谢谢!

特里夫。

3 个答案:

答案 0 :(得分:2)

我最近为此创建了一个grails插件。 https://github.com/grails-plugins/grails-embedded-mongodb

目前它是快照,但我打算本周发布一个版本

答案 1 :(得分:0)

我使用内存中的Mongo服务器进行集成测试,效果很好。它运行速度快,不需要启动单独的Mongo服务器或处理特殊的grails或maven配置。这意味着测试可以与任何JUnit测试运行器同样良好地运行,即在任何IDE或构建系统中运行。无需额外设置。

In-memory Mongo example

我也使用了" flapdoodle"嵌入式mongo服务器进行测试。它使用不同的方法,因为它为真正的Mongo实例下载并执行单独的进程。我发现这个机制有更多移动部件,而且当我真正想做的就是验证我的应用程序与mongo服务器是否正常工作时,对我来说似乎有些过分。

答案 2 :(得分:0)

迟到最好的回答 -

不幸的是,我发现Fongo并没有很好地满足我的所有要求 - 最值得注意的是,$eval未实现,因此您无法使用Mongeez等迁移工具运行集成测试。

我选择EmbedMongo,我通过JUnit ExternalResource规则在我的Spock / Geb集成测试中使用它。尽管加里是正确的,他说真正的托管数据库带有更多移动部件,但我发现我宁愿冒这个风险,也不愿依靠模拟实现。到目前为止,它工作得很好,在测试套件拆解期间提供或采取不干净的数据库关闭,幸运的是,这不会影响测试。您可以按如下方式使用规则:

@Integration(applicationClass = Application)
@TestFor(SomeGrailsArtifact)  // this will inject grailsApplication
class SomeGrailsArtifactFunctionalSpec extends Specification {

    @Shared @ClassRule
    EmbedMongoRule embedMongoRule = new EmbedMongoRule(grailsApplication)

    @Rule
    ResetDatabaseRule resetDatabaseRule = new ResetDatabaseRule(embedMongoRule.db)
    ...

为了完整起见,这些是规则实现:

EmbedMongoRule.groovy

import org.junit.rules.ExternalResource

import com.mongodb.MongoClient
import com.mongodb.MongoException

import de.flapdoodle.embed.mongo.MongodProcess
import de.flapdoodle.embed.mongo.MongodStarter
import de.flapdoodle.embed.mongo.config.IMongodConfig
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

/**
 * Rule for {@code EmbedMongo}, a managed full-fledged MongoDB. The first time
 * this rule is used, it will download the current production MongoDB release,
 * spin it up before tests and tear it down afterwards.
 * 
 * @author Michael Jess
 *
 */
public class EmbedMongoRule extends ExternalResource {

    private def mongoConfig
    private def mongodExecutable

    public EmbedMongoRule(grailsApplication) {
        if(!grailsApplication) {
            throw new IllegalArgumentException(
                "Got null grailsApplication; have you forgotten to supply it to the rule?\n" +
                "\n" +
                "@Integration(applicationClass = Application)\n" +
                "@TestFor(MyGrailsArtifact)\n // will inject grailsApplication" +
                "class MyGrailsArtifactSpec extends ... {\n" +
                "\n" +
                "\t..." +
                "\t@Shared @ClassRule EmbedMongoRule embedMongoRule = new EmbedMongoRule(grailsApplication)\n" +
                "\t...\n" +
                "}")
        }
        mongoConfig = grailsApplication.config.grails.mongodb
    }

    @Override
    protected void before() throws Throwable {
        try {
            MongodStarter starter = MongodStarter.getDefaultInstance()

            IMongodConfig mongodConfig = new MongodConfigBuilder()
                .version(Version.Main.PRODUCTION)
                .net(new Net(mongoConfig.port, Network.localhostIsIPv6()))
                .build()

            mongodExecutable = starter.prepare(mongodConfig)
            MongodProcess mongod = mongodExecutable.start()
        } catch (IOException e) {
            throw new IllegalStateException("Unable to start embedded mongo", e)
        }
    }

    @Override
    protected void after() {
        mongodExecutable.stop()
    }

    /**
     * Returns a new {@code DB} for the managed database.
     * 
     * @return A new DB
     * @throws IllegalStateException If an {@code UnknownHostException}
     *  or a {@code MongoException} occurs
     */
    public def getDb() {
        try {
            return new MongoClient(mongoConfig.host, mongoConfig.port).getDB(mongoConfig.databaseName)
        } catch (UnknownHostException | MongoException e) {
            throw new IllegalStateException("Unable to retrieve MongoClient", e)
        }
    }

}

ResetDatabaseRule.groovy - 自GORM ignores the grails.mongodb.databaseName parameter as of org.grails.plugins:mongodb:4.0.0 (grails 3.x)以来目前无效

import org.junit.rules.ExternalResource

/**
 * Rule that will clear whatever Mongo {@code DB} is provided.
 * More specifically, all non-system collections are dropped from the database.
 *
 * @author Michael Jess
 *
 */
public class ResetDatabaseRule extends ExternalResource {

    /**
     * Prefix identifying system tables
     */
    private static final String SYSTEM_TABLE_PREFIX = "system"

    private def db

    /**
     * Create a new database reset rule for the specified datastore.
     *
     * @param getDb Closure returning a reference to the {@link DB} instance
     * to reset.
     */
    ResetDatabaseRule(db) {
        this.db = db
    }

    @Override
    protected void before() throws Throwable {
        db.collectionNames
            .findAll { !it.startsWith(SYSTEM_TABLE_PREFIX) }
            .each { db.getCollection(it).drop() }
    }

}