76

私が選んだデータベースはMongoDBです。クライアントアプリケーションから実装の詳細を抽象化するデータ層APIを作成しています。つまり、基本的に単一のパブリックインターフェイス(IDLとして機能するオブジェクト)を提供しています。

TDD方式で実行しながら、ロジックをテストしています。各単体テストの前に@Before、データベースシングルトンを作成するためのメソッドが呼び出されます。その後、テストが完了すると@After、データベースを削除するためのメソッドが呼び出されます。これは、単体テスト間の独立性を促進するのに役立ちます。

ほぼすべての単体テスト、つまりコンテキストクエリの実行では、事前に何らかの挿入ロジックを実行する必要があります。私のパブリックインターフェイスは挿入メソッドを提供しますが、このメソッドを各単体テストのプリカーサーロジックとして使用するのは正しくないようです。

本当に何らかのモックメカニズムが必要ですが、モックフレームワークの経験はあまりなく、MongoDBで使用するようなモックフレームワークについてはGoogleが何も返さないようです。

これらの状況で他の人は何をしますか?つまり、人々はデータベースと相互作用するコードをどのように単体テストするのでしょうか。

また、パブリックインターフェイスは、外部構成ファイルで定義されたデータベースに接続します。この接続を単体テストに使用するのは正しくないようです。これも、ある種のモックの恩恵を受ける状況ですか?

4

5 に答える 5

71

技術的には、データベース(nosqlなど)と通信するテストは単体テストではありませんこれは、テストが外部システムとの相互作用をテストするものであり、分離されたコードのユニットをテストするだけではないためです。ただし、データベースと通信するテストは非常に便利なことが多く、他の単体テストで実行するのに十分な速度であることがよくあります。

通常、データベースを処理するためのすべてのロジックをカプセル化するサービスインターフェイス(UserServiceなど)があります。UserServiceに依存するコードは、モックバージョンのUserServiceを使用でき、簡単にテストできます。

Mongoと通信するサービス(MongoUserServiceなど)の実装をテストする場合、ローカルマシンでmongoプロセスを開始/停止するJavaコードを記述し、MongoUserServiceをそれに接続させるのが最も簡単です。ノート

MongoUserServiceのテスト中にデータベースの機能をモックしようとすることもできますが、一般的にはエラーが発生しやすく、実際のデータベースとの相互作用である実際にテストしたいものはテストされません。したがって、MongoUserServiceのテストを作成するときは、テストごとにデータベースの状態を設定します。データベースを使用してこれを行うためのフレームワークの例については、 DbUnitを参照してください。

于 2011-11-25T06:13:41.057 に答える
34

sbridges がこの投稿で書いたように、ロジックからデータ アクセスを抽象化する専用サービス (リポジトリまたは DAO とも呼ばれます) を持たないのは悪い考えです。次に、DAO のモックを提供してロジックをテストできます。

私が行うもう 1 つのアプローチは、Mongo オブジェクト (PowerMockito など) のモックを作成し、適切な結果を返すことです。これは、データベースが単体テストで機能するかどうかをテストする必要はありませんが、正しいクエリがデータベースに送信されたかどうかをテストする必要があるためです。

Mongo mongo = PowerMockito.mock(Mongo.class);
DB db = PowerMockito.mock(DB.class);
DBCollection dbCollection = PowerMockito.mock(DBCollection.class);

PowerMockito.when(mongo.getDB("foo")).thenReturn(db);
PowerMockito.when(db.getCollection("bar")).thenReturn(dbCollection);

MyService svc = new MyService(mongo); // Use some kind of dependency injection
svc.getObjectById(1);

PowerMockito.verify(dbCollection).findOne(new BasicDBObject("_id", 1));

それも一つの選択肢でしょう。もちろん、モックの作成と適切なオブジェクトの返却は、上記の例のようにコーディングされています。

于 2011-11-25T07:29:16.390 に答える
22

Java で MongoDB の偽の実装を作成しました: mongo-java-server

デフォルトはインメモリ バックエンドで、単体テストと統合テストで簡単に使用できます。

MongoServer server = new MongoServer(new MemoryBackend());
// bind on a random local port
InetSocketAddress serverAddress = server.bind();

MongoClient client = new MongoClient(new ServerAddress(serverAddress));

DBCollection coll = client.getDB("testdb").getCollection("testcoll");
// creates the database and collection in memory and inserts the object
coll.insert(new BasicDBObject("key", "value"));

assertEquals(1, collection.count());
assertEquals("value", collection.findOne().get("key"));

client.close();
server.shutdownNow();
于 2013-02-20T17:29:49.633 に答える
3

これまでのところ、 fakemongoの使用を勧められた人が誰もいなかったことに驚いています。これは mongo クライアントをかなりうまくエミュレートし、すべて同じ JVM でテストを実行します。そのため、外部システムとの相互作用が発生しないため、統合テストが堅牢になり、技術的に真の「単体テスト」にはるかに近くなります。これは、埋め込まれた H2 を使用して SQL コードの単体テストを行うようなものです。データベース統合コードをエンドツーエンドでテストする単体テストで、fakemongo を使用できてとても満足しています。テストスプリングのコンテキストでこの構成を検討してください。

@Configuration
@Slf4j
public class FongoConfig extends AbstractMongoConfiguration {
    @Override
    public String getDatabaseName() {
        return "mongo-test";
    }

    @Override
    @Bean
    public Mongo mongo() throws Exception {
        log.info("Creating Fake Mongo instance");
        return new Fongo("mongo-test").getMongo();
    }

    @Bean
    @Override
    public MongoTemplate mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), getDatabaseName());
    }

}

これにより、Spring コンテキストから MongoTemplate を使用するコードをテストでき、nosql-unitjsonunitなどと組み合わせて、mongo クエリ コードをカバーする堅牢な単体テストを取得できます。

@Test
@UsingDataSet(locations = {"/TSDR1326-data/TSDR1326-subject.json"}, loadStrategy = LoadStrategyEnum.CLEAN_INSERT)
@DatabaseSetup({"/TSDR1326-data/dbunit-TSDR1326.xml"})
public void shouldCleanUploadSubjectCollection() throws Exception {
    //given
    JobParameters jobParameters = new JobParametersBuilder()
            .addString("studyId", "TSDR1326")
            .addString("execId", UUID.randomUUID().toString())
            .toJobParameters();

    //when
    //next line runs a Spring Batch ETL process loading data from SQL DB(H2) into Mongo
    final JobExecution res = jobLauncherTestUtils.launchJob(jobParameters);

    //then
    assertThat(res.getExitStatus()).isEqualTo(ExitStatus.COMPLETED);
    final String resultJson = mongoTemplate.find(new Query().with(new Sort(Sort.Direction.ASC, "topLevel.subjectId.value")),
            DBObject.class, "subject").toString();

    assertThatJson(resultJson).isArray().ofLength(3);
    assertThatDateNode(resultJson, "[0].topLevel.timestamp.value").isEqualTo(res.getStartTime());

    assertThatNode(resultJson, "[0].topLevel.subjectECode.value").isStringEqualTo("E01");
    assertThatDateNode(resultJson, "[0].topLevel.subjectECode.timestamp").isEqualTo(res.getStartTime());

    ... etc
}

私は mongo 3.4 ドライバーで問題なく fakemongo を使用しており、コミュニティは 3.6 ドライバーをサポートするバージョンをリリースするのに非常に近いです ( https://github.com/fakemongo/fongo/issues/316 )。

于 2018-03-20T21:32:17.460 に答える