IT TIP

MongoDB를 사용한 단위 테스트

itqueen 2020. 12. 7. 21:21
반응형

MongoDB를 사용한 단위 테스트


내 데이터베이스는 MongoDB입니다. 클라이언트 응용 프로그램에서 구현 세부 정보를 추상화하기 위해 데이터 계층 API를 작성하고 있습니다. 즉, 기본적으로 단일 공용 인터페이스 (IDL 역할을하는 개체)를 제공하고 있습니다.

TDD 방식으로 진행하면서 내 논리를 테스트하고 있습니다. 각 단위 테스트 전에 @Before메서드가 호출되어 데이터베이스 싱글 톤을 생성 한 후 테스트가 완료되면 @After메서드가 호출되어 데이터베이스를 삭제합니다. 이는 단위 테스트 간의 독립성을 높이는 데 도움이됩니다.

거의 모든 단위 테스트 (예 : 컨텍스트 쿼리 수행)에는 미리 발생하는 일종의 삽입 논리가 필요합니다. 내 공용 인터페이스는 삽입 방법을 제공하지만이 방법을 각 단위 테스트의 전조 논리로 사용하는 것은 잘못된 것 같습니다.

정말 어떤 종류의 조롱 메커니즘이 필요하지만 조롱 프레임 워크에 대한 경험이 많지 않았고 Google은 MongoDB에서 사용할 수있는 조롱 프레임 워크에 대해 아무것도 반환하지 않는 것 같습니다.

이러한 상황에서 다른 사람들은 무엇을합니까? 즉, 사람들은 데이터베이스와 상호 작용하는 코드 단위 테스트를 어떻게합니까?

또한 내 공용 인터페이스는 외부 구성 파일에 정의 된 데이터베이스에 연결됩니다.이 연결을 단위 테스트에 사용하는 것은 잘못된 것 같습니다. 다시 말하지만, 일종의 조롱으로 이익을 얻을 수있는 상황입니까?


sbridges가이 게시물에서 썼 듯이 논리에서 데이터 액세스를 추상화하는 전용 서비스 (저장소 또는 DAO라고도 함)를 사용하지 않는 것은 좋지 않습니다. 그런 다음 DAO의 모의를 제공하여 논리를 테스트 할 수 있습니다.

내가하는 또 다른 접근법은 Mongo 객체 (예 : PowerMockito)의 Mock을 생성 한 다음 적절한 결과를 반환하는 것입니다. 이는 데이터베이스가 단위 테스트에서 작동하는지 테스트 할 필요가 없지만 올바른 쿼리가 databse로 전송되었는지 테스트해야하기 때문입니다.

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));

그것은 또한 옵션이 될 것입니다. 물론 모의 생성과 적절한 객체의 반환은 위의 예제처럼 코딩되었습니다.


기술적으로 데이터베이스 (nosql 또는 기타) 와 통신하는 테스트는 단위 테스트 가 아닙니다. 테스트는 격리 된 코드 단위를 테스트하는 것이 아니라 외부 시스템과의 상호 작용을 테스트하기 때문입니다. 그러나 데이터베이스와 통신하는 테스트는 종종 매우 유용하며 다른 단위 테스트와 함께 실행하기에 충분히 빠릅니다.

일반적으로 데이터베이스를 처리하기위한 모든 논리를 캡슐화하는 서비스 인터페이스 (예 : UserService)가 있습니다. UserService에 의존하는 코드는 UserService의 모의 버전을 사용할 수 있으며 쉽게 테스트됩니다.

서비스의 구현을 테스트 할 때 그 몽고, (예를 들어 MongoUserService)이 /가 시작됩니다 일부 자바 코드를 작성 로컬 컴퓨터에 몽고 프로세스를 중지하고 MongoUserService가 연결이이 볼 수있는 가장 쉬운 방법입니다 회담 일부에 대한 질문 메모 .

MongoUserService를 테스트하는 동안 데이터베이스의 기능을 모의 할 수 있지만 일반적으로 오류가 발생하기 쉽고 실제 데이터베이스와의 상호 작용 인 실제로 테스트하려는 것을 테스트하지 않습니다. 따라서 MongoUserService에 대한 테스트를 작성할 때 각 테스트에 대한 데이터베이스 상태를 설정합니다. DbUnit을 데이터베이스와 그렇게하기위한 프레임 워크의 예.


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();

오늘날 가장 좋은 방법은 Python에서 testcontainers 라이브러리 (Java) 또는 testcontainers-python 포트를 사용하는 것입니다. 단위 테스트와 함께 Docker 이미지를 사용할 수 있습니다. Java 코드에서 컨테이너를 실행하려면 GenericContainer 객체를 인스턴스화하기 만하면됩니다 ( example ).

GenericContainer mongo = new GenericContainer("mongo:latest")
    .withExposedPorts(27017);

MongoClient mongoClient = new MongoClient(mongo.getContainerIpAddress(), mongo.getMappedPort(27017));
MongoDatabase database = mongoClient.getDatabase("test");
MongoCollection<Document> collection = database.getCollection("testCollection");

Document doc = new Document("name", "foo")
        .append("value", 1);
collection.insertOne(doc);

Document doc2 = collection.find(new Document("name", "foo")).first();
assertEquals("A record can be inserted into and retrieved from MongoDB", 1, doc2.get("value"));

또는 Python ( ) :

mongo = GenericContainer('mongo:latest')
mongo.with_bind_ports(27017, 27017)

with mongo_container:
    def connect():
        return MongoClient("mongodb://{}:{}".format(mongo.get_container_host_ip(),
                                                    mongo.get_exposed_port(27017)))

    db = wait_for(connect).primer
    result = db.restaurants.insert_one(
        # JSON as dict object
    )

    cursor = db.restaurants.find({"field": "value"})
    for document in cursor:
        print(document)

I'm surprised no one advised to use fakemongo so far. It emulates mongo client pretty well, and it all runs on same JVM with tests - so integration tests become robust, and technically much more close to true "unit tests", since no foreign system interaction takes place. It's like using embedded H2 to unit test your SQL code. I was very happy using fakemongo in unit tests that test database integration code in end-to-end manner. Consider this configuration in test spring context:

@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());
    }

}

With this you can test your code that uses MongoTemplate from spring context, and in combination with nosql-unit, jsonunit, etc. you get robust unit tests that cover mongo querying code.

@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
}

I used fakemongo without problems with mongo 3.4 driver, and community is really close to release a version that supports 3.6 driver (https://github.com/fakemongo/fongo/issues/316).

참고URL : https://stackoverflow.com/questions/7413985/unit-testing-with-mongodb

반응형