2017-02-13 4 views
3

유형을 조롱하기 위해 인터페이스를 정의해야한다고 읽었지만이를 해석하는 방법을 모르겠습니다.
예를 들어 FileSystem을 모의하기 위해 직접 I/O 메서드를 호출하는 대신 개체를 메서드에 전달한 다음 테스트에서 호출 할 때이를 모의 할 수 있습니다. 단위 테스트의 예가 (아래의 SO 질문과 유사) 인터페이스를 사용하는 이유는 무엇입니까?유닛 테스트에서 mock에 인터페이스가 필요합니까?

void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner) 
{ 
    string path = zipper.Unzip(theZipFile); 
    IFakeFile file = fileSystem.Open(path); 
    runner.Run(file); 
} 

단순히 각 유형의 인수를 사용하여 객체를 삽입 할 수 없습니까?

예. GUICE 가이드에서 생성자 작업 (예 : 'new'키워드를 사용하는 입력란 제외)을 삭제하고 istead 매개 변수를 사용하는 것이 좋습니다.

+1

그런 다음 객체를 주입하십시오. 예,하지만 객체에 모의 주입 방법은 무엇입니까? 적어두고 얼마나 멀리 있는지보십시오. –

+0

유닛 테스트를 호출 할 때, 나는 단순히 객체 (구체적인 구현체)를 만들고 그것을 전달할 것으로 기대한다. – user970696

+1

@HenkHolterman 실제 인스턴스를 만드는 것은 "라이브", 종속성 등을 의미합니다. 인터페이스를 사용하여 필자가 필요로하는 응답을 조롱하며 자신의 동작을 정의합니다. – user970696

답변

3

조롱 프레임 워크에 따라 다릅니다. Moq을 사용하면 밀폐되어 있지 않고 모의하려는 멤버가 가상 인 한 콘크리트 유형을 조롱 할 수 있습니다. 인터페이스는 일반적으로 멤버가 가상으로 표시 할 필요가 없기 때문에 선호됩니다. (느슨한 수업 사이의 결합을 유지하는 등의 인터페이스를 선호하는 또 다른 이유가 있습니다) 업데이트

https://github.com/Moq/moq4/wiki/Quickstart

일반적으로 당신은 유형의 인스턴스를 조롱합니다. 따라서 모의 인스턴스가 없으므로 FileStream.Open과 같은 정적 메서드 호출을 테스트 할 수 없습니다. MOQ 같은

업데이트

프레임 워크는 모의 객체를 생성하기 위해 상속에 의존하고 있습니다. 그렇기 때문에 메소드가 가상이어야합니다. 정적 또는 개인 메서드를 조롱하려면 Microsoft Fakes를 사용해보십시오.

https://msdn.microsoft.com/en-us/library/hh549175.aspx

+0

고마워, 내 질문은 오히려 프레임 워크 불가 지론이다. 구체적인 구현을 사용하여 문제가 무엇인지 알고 싶습니다 (일부 경우에는 파일 시스템, DB 연결 등). – user970696

+0

하지만 구체적인 유형을 사용하면 더 큰 문제가 있음을 알 수 있습니다. 실제 인스턴스를 만들어야합니다. , 그것은 외부 의존성 등이있을 수 있습니다. 나는 단순히 DateTime이 매번 동일한 시간을 반환하도록 말할 수 없다는 것을 의미합니다. 인터페이스를 사용하는 가장 큰 이유는 무엇입니까? – user970696

+0

DateTime은 힘든 시간입니다. 나는 보통 인터페이스 호출로 랩핑한다. –

2

이없는 종속성과 같은 인터페이스를 가지고해야하지만, 그 권장 널리 연습을 따라합니다.

종속성으로 인터페이스 및 기본 클래스를 사용하면 손실이 많은 커플 디자인을 만드는 데 도움이됩니다. 느슨하게 결합 된 디자인은 다른 레이어에 대한 구현 세부 사항에 의존하지 않고 모듈/애플리케이션 레이어를 생성 할 수 있습니다. 이렇게하면 다른 레이어가 작동하거나 반응하는 것을 걱정하지 않고도 코드 조각을 쉽게 테스트 할 수 있습니다. 이러한 유형의 테스트를 단위 테스트라고합니다.

콘크리트 클래스에 의존성이 있다면 단위 테스트 코드를 분리하여 사용할 수 없습니다. 이러한 테스트는 종속성 구현이 변경 될 때마다 실패하는 경향이 있습니다. 코드가 실제 파일 시스템 클래스 또는 실제 db에 연결되는 데이터 액세스 클래스에 종속되는 경우 파일에 액세스 할 수 없거나 db 서버가 다운되거나 데이터가 손상 될 때 단위 테스트가 실패 할 가능성이 있습니다.

단위 테스트라고는하지 않습니다. 이를 통합 테스트라고합니다.

앞서 언급 한 것처럼 별도로 코드를 테스트하는 동안 종속성이 어떻게 작동하는지 걱정할 필요가 없습니다. 그 이유는 단위 테스트 종속성을 조롱하는 동안. 또한 종속성은 인터페이스가 아니어야합니다. 기본 클래스가 될 수도 있습니다. 종속성을 조롱 할 때, mock은 기능을 실제로 구현하지 않습니다. mock을 만들 수있는 방법은 여러 가지가 있습니다.

단위 테스트에서 mocks를 사용하면 현재 클래스의 코드를 테스트하는 동안 기대 한대로 동작하는지 확인해야합니다. 따라서 모의 객체가 원하는 방식으로 동작하도록 요청하십시오. 모의 메소드 나 속성이 호출되었는지 확인하십시오. 그러면 테스트중인 코드 조각의 모든 경로를 처리 할 수 ​​있습니다.

다음은 종속성이있는 단위 테스트의 간단한 예입니다.

public class ProductService 
{ 
    private IProductRepository repository; 
    public ProductService (IProductRepository repository) 
    { 
     this.repository = repository; 
    } 

    public Product GetProductById(int productId) 
    { 
     if(productId <= 0) 
      return null; 

     var product = this.repository.GetProductById(productId); 

     return product; 
    } 
} 

내가 IProductRepository 인터페이스의 실제 구현이없는 경우에도, ProductService 클래스를 생성하는 동안, 나는 여전히 다음과 같은 수 있습니다 단위 테스트 ProductService 클래스로.

public class ProductServiceTests 
{ 
    ProductService serviceToTest; 
    IProductRepository productRepository; 

    public ProductServiceTests() 
    { 
     this.productRepository = Mock<IProductRepository>(); 
     this.serviceToTest = new ProductService(productRepository); 
    } 

    public void GetProductByIdReturnsNullIfProductIdIsZero() 
    { 
     int productid = 0; 

     var product = serviceToTest.GetProductById(productid); 
     Assert.That(product, Is.Null); 
    } 

    public void GetProductByIdReturnsNullIfProductIdIsNegative() 
    { 
     int productid = -1; 
     var product = serviceToTest.GetProductById(productid); 
     Assert.That(product, Is.Null); 
    } 

    public void GetProductByIdReturnsProductIfProductIdIsPositive() 
    { 
     var productid = 1; 

     var product = new Product { Id = productId }; 

     productRepository.Setup(repo => repo.GetProductById(productid)).Returns(product); //Making sure that GetProductById method of repository is called and return an object of product as this call is part of the code which I am testing right now. 

     var product = serviceToTest.GetProductById(productid); 

     Assert.That(product, Is.Not.Null); 
     Assert.That(product.Id, Is.EqualTo<int>(productid)); 
    } 
} 

이것은 단위 테스트를 설명하기위한 예제 코드입니다. 예상대로 빌드되거나 실행되지 않을 수 있으며 사과드립니다.

+0

고마워요. 따라서 예상되는 매개 변수 저장소가 인터페이스가 아니고 실제 유형 저장소 인 경우 계속 조롱 할 수 있습니까? – user970696

+0

예, 봉인 된 클래스가 아닌 한 조롱 할 수 있습니다. 그래도 권장되는 디자인 연습은 아닙니다. 서비스가 새 저장소에 대해 알 수 있도록 코드를 변경해야 할 때 기존 저장소를 대체 할 새 저장소를 도입 할 때 문제가됩니다. 그것은 당신의 단위 테스트도 깨뜨릴 것입니다. 인터페이스를 통해 이러한 경우는 서비스 클래스를 변경하지 않고 DI 컨테이너의 구성을 변경하여 쉽게 처리 할 수 ​​있습니다. –

+0

하지만 어떻게? 참조가되는 객체가 상당히 복잡하면 (예 : 생성자에서 많은 매개 변수가 필요함) – user970696

관련 문제