2016-12-04 5 views
0

이것은 처음으로 단위 테스트를 작성하는 것이므로 몇 가지 질문이 있습니다. 내 서비스를 테스트하기 위해 메모리 데이터베이스를 사용하고 있으며, 올바르게 작동하는지 궁금합니다. 내 첫 번째 질문은 내 서비스 요청에 대해 여러 번 주장해야한다는 것입니다. 내가 InsertProduct에 대한 주장이 필요합니까? 둘째, 모든 서비스 호출에서 컨텍스트의 새로운 인스턴스를 사용하여이를 테스트하고 있습니까?다중 어설트가 필요합니까? xUnit 테스트

[Fact] 
public void ProductService_DeleteProduct_Test() 
{ 
    // arrange 
    var options = new DbContextOptionsBuilder<ApplicationDbContext>() 
     .UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test") 
     .Options; 

    var product = new Product() { Id = Guid.NewGuid(), Name = "Product"}; 

    // act 
    // insert 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.InsertProduct(product); 
    } 

    // delete 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.DeleteProducts(new List<Guid> { product.Id }); 
    } 

    // assert 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     Assert.Equal(0, service.ProductService.GetAllProducts().Count); 
    } 
} 

답변

1

나는 귀하의 테스트 구조에 이의를 제기 할 것입니다. 즉, 서비스 (프로덕션 코드)를 사용하여 기본 데이터베이스를 준비하고 있습니다. 또한 프로덕션 코드를 사용하여 어설 션을 만듭니다.

프로덕션 코드의 일부가 올바르지 않으면이 테스트가 실패합니다. 그러나이 테스트는 삭제 기능이 올바르게 작동하는지 확인하도록 설계되었습니다.

따라서, 나는 다음과 같은 방법으로 전체 테스트를 다시 작성합니다 : 이런 식으로

[Fact] 
public void ProductService_DeleteProduct_Test() 
{ 
    // arrange 
    var options = new DbContextOptionsBuilder<ApplicationDbContext>() 
     .UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test") 
     .Options; 

    var product = new Product() { Id = Guid.NewGuid(), Name = "Product"}; 

    // Insert object using other means, i.e. direct INSERT statement 

    // act 
    using (var context = new ApplicationDbContext(options)) 
    { 
     var service = new Service(context); 
     service.ProductService.DeleteProducts(new List<Guid> { product.Id }); 
    } 

    // assert 
    // Execute SELECT COUNT(*) instruction to fetch previously existing row 
    Assert.Equal(0, rowsCount); 
} 

, 당신은 단지 시험의 역할을 일부 생산 코드를 만지지 것입니다. 이것이 서비스 객체를 사용하여 데이터베이스에서 객체를 삭제하는 부분입니다.

이후의 어설 션은 저장 장치에서 직접 실행 된 원시 SELECT 문의 결과로 가져 오는 스칼라 값 count에 대해 수행됩니다.

결론은 사실 현재 테스트중인 방법 인 DeleteProducts 메서드를 제외하고는 테스트 코드 중 어느 부분도 프로덕션 코드의 정확성에 의존하지 않는다는 것입니다.

그리고 결과적으로 귀하의 질문에 대한 대답은이 테스트에서 단 하나의 단언을 작성한다는 것입니다.

+0

질문을 받아들이 기 전에 필자는 엔티티 프레임 워크를 사용하여 내 테스트를 수정하기 때문에'context.Database.ExecuteSqlCommand' 또는'context.Database.SqlQuery'를 사용해야하거나'using System '을 사용해야 만합니다. Data.SqlClient; ' – kram005

+0

가능합니다. 그러나 과거에 테스트 프로젝트에서 별도의 연결 문자열을 사용했던 테스트 스위트가 있었지만 원시 SQL을 실행하는 데 'SqlCommands'를 사용했습니다. 따라서 테스트와 생산 코드 간의 마지막 연결조차도 제거됩니다. 어쨌든, 이것은 단지 세부 사항이며 당신은 기존의'DbContext'로 작업 할 것입니다. –

+0

메모리 데이터베이스에서 ef를 사용하고 있기 때문에 dbcontext를 계속 사용해야하고 sqlcommand를 사용하여 메모리 데이터베이스에서 수행하는 방법을 모릅니다. – kram005

1

첫 번째 질문에 답하면 아니오라고 대답합니다. 이것은 단위 테스트이므로 삭제를 테스트하고 있습니다. 나는 당신이 삭제를 시험하고 싶은 국가로 시스템을 얻고 있기 때문에 셋업의 삽입 부분을 고려한다. Zoran Horvat의 관점에서, 가능한 경우, 서비스 자체 이외의 다른 수단을 통해 데이터베이스에 행을 추가하십시오.

두 번째 질문에 대답하려면 서비스를 3 번 ​​new'ing하는 블록을 3 개 사용하는 것이 불필요한 것 같습니다. 나는 insert, delete, assert를 SUT 나 서비스의 하나의 인스턴스를 사용하여 동일하게 사용했다.

그러나 데이터베이스에 행이 모두 있어야하는 여러 개의 테스트가있는 경우 모든 테스트가 직접 호출 할 수있는 [SetUp] 특성을 사용하여 삽입을 SetUp 메서드로 옮기는 것이 좋습니다. 이 경우 컨텍스트의 여러 인스턴스를 사용하게됩니다.

관련 문제