NUnit - 특정 인터페이스를 구현하는 모든 클래스를 테스트하는 방법

StackOverflow https://stackoverflow.com/questions/39003

  •  09-06-2019
  •  | 
  •  

문제

IFoo 인터페이스가 있고 이를 구현하는 여러 클래스가 있는 경우 인터페이스에 대해 모든 클래스를 테스트하는 가장 좋고/가장 우아하고/가장 영리한 방법은 무엇입니까?

테스트 코드 중복을 줄이고 싶지만 여전히 단위 테스트 원칙을 '준수'하고 싶습니다.

모범 사례는 무엇이라고 생각하시나요?NUnit을 사용하고 있지만 모든 단위 테스트 프레임워크의 예제가 유효할 것이라고 가정합니다.

도움이 되었습니까?

해결책

클래스가 하나의 인터페이스를 구현하는 경우 해당 인터페이스의 메서드를 모두 구현해야 합니다.이러한 클래스를 테스트하려면 각 클래스에 대한 단위 테스트 클래스를 만들어야 합니다.

대신 더 똑똑한 경로를 선택해 보겠습니다.당신의 목표가 있다면 코드 및 테스트 코드 중복 방지 대신에 다음을 처리하는 추상 클래스를 만들고 싶을 수도 있습니다. 반복되는 암호.

예:다음과 같은 인터페이스가 있습니다.

public interface IFoo {

    public void CommonCode();

    public void SpecificCode();

}

추상 클래스를 만들고 싶을 수도 있습니다.

public abstract class AbstractFoo : IFoo {

    public void CommonCode() {
          SpecificCode();
    }

    public abstract void SpecificCode();

}

테스트는 쉽습니다.테스트 클래스의 추상 클래스를 내부 클래스로 구현합니다.

[TestFixture]
public void TestClass {

    private class TestFoo : AbstractFoo {
        boolean hasCalledSpecificCode = false;
        public void SpecificCode() {
            hasCalledSpecificCode = true;
        }
    }

    [Test]
    public void testCommonCallsSpecificCode() {
        TestFoo fooFighter = new TestFoo();
        fooFighter.CommonCode();
        Assert.That(fooFighter.hasCalledSpecificCode, Is.True());
    }
}

...또는 원하는 경우 테스트 클래스가 추상 클래스 자체를 확장하도록 하세요.

[TestFixture]
public void TestClass : AbstractFoo {

    boolean hasCalledSpecificCode;
    public void specificCode() {
        hasCalledSpecificCode = true;
    }

    [Test]
    public void testCommonCallsSpecificCode() {
        AbstractFoo fooFighter = this;
        hasCalledSpecificCode = false;
        fooFighter.CommonCode();
        Assert.That(fooFighter.hasCalledSpecificCode, Is.True());
    }        

}

인터페이스가 암시하는 공통 코드를 추상 클래스로 처리하면 코드 디자인이 훨씬 깔끔해집니다.

이것이 당신에게 의미가 있기를 바랍니다.


참고로, 이것은 다음과 같은 일반적인 디자인 패턴입니다. 템플릿 메소드 패턴.위의 예에서 템플릿 방법은 CommonCode 방법과 SpecificCode 스텁(stub) 또는 후크(hook)라고 합니다.그 아이디어는 누구나 배후를 알 필요 없이 행동을 확장할 수 있다는 것입니다.

많은 프레임워크가 이 동작 패턴에 의존합니다. ASP.NET 페이지나 생성된 컨트롤과 같은 사용자 컨트롤에 후크를 구현해야 하는 경우 Page_Load 에 의해 호출되는 메소드 Load 이벤트가 발생하면 템플릿 메소드는 배후에서 후크를 호출합니다.이에 대한 더 많은 예가 있습니다.기본적으로 "load", "init" 또는 "render"라는 단어를 사용하여 구현해야 하는 모든 항목은 템플릿 메서드에 의해 호출됩니다.

다른 팁

나는 동의하지 않는다 존 림잡 그가 말할 때,

이는 a.) 메소드가 어떻게 구현되어야 하는지와 b.) 해당 메소드가 정확히 무엇을 해야 하는지(반환 유형만 보장함)에 대한 계약이 아닙니다. 제가 수집한 두 가지 이유는 이러한 종류를 원하는 동기가 될 것입니다. 테스트의.

반환 유형에 지정되지 않은 계약 부분이 많을 수 있습니다.언어에 구애받지 않는 예:

public interface List {

  // adds o and returns the list
  public List add(Object o);

  // removed the first occurrence of o and returns the list
  public List remove(Object o);

}

LinkedList, ArrayList, CircularlyLinkedList 및 기타 모든 항목에 대한 단위 테스트는 목록 자체가 반환되는지뿐만 아니라 적절하게 수정되었는지도 테스트해야 합니다.

있었다 이전 질문 설계별 계약을 통해 이러한 테스트를 마무리하는 한 가지 방법에 대한 올바른 방향을 제시하는 데 도움이 될 수 있습니다.

계약에 따른 간접비를 원하지 않는다면 테스트 장비를 추천합니다. 스포이크 권장사항:

abstract class BaseListTest {

  abstract public List newListInstance();

  public void testAddToList() {
    // do some adding tests
  }

  public void testRemoveFromList() {
    // do some removing tests
  }

}

class ArrayListTest < BaseListTest {
  List newListInstance() { new ArrayList(); }

  public void arrayListSpecificTest1() {
    // test something about ArrayLists beyond the List requirements
  }
}

나는 이것이 모범 사례라고 생각하지 않습니다.

단순한 진실은 인터페이스가 메소드가 구현되는 계약에 지나지 않는다는 것입니다.그것은 ~ 아니다 a.) 메소드를 구현하는 방법 b.) 해당 메소드가 정확히 무엇을 수행해야 하는지(반환 유형만 보장함)에 대한 계약, 제가 수집한 두 가지 이유는 이러한 종류의 테스트를 원하는 동기가 될 것입니다.

메소드 구현을 실제로 제어하려면 다음 옵션을 사용할 수 있습니다.

  • 추상 클래스의 메서드로 구현하고 그로부터 상속받습니다.여전히 이를 구체적인 클래스로 상속해야 하지만 명시적으로 재정의하지 않는 한 해당 메서드가 올바른 작업을 수행할 것이라고 확신합니다.
  • .NET 3.5/C# 3.0에서는 인터페이스를 참조하는 확장 메서드로 메서드를 구현합니다.

예:

public static ReturnType MethodName (this IMyinterface myImplementation, SomeObject someParameter)
{
    //method body goes here
}

해당 확장 메서드를 올바르게 참조하는 모든 구현은 해당 확장 메서드를 정확하게 생성하므로 한 번만 테스트하면 됩니다.

@황제XLII

저는 MbUnit의 조합 테스트 소리를 좋아합니다. NUnit을 사용하여 추상 기본 클래스 인터페이스 테스트 기술을 시도해 보았지만 작동하더라도 클래스가 구현하는 모든 인터페이스에 대해 별도의 테스트 픽스쳐가 필요합니다(C#에서는 다중 상속은 없습니다. 비록 내부 클래스를 사용할 수 있지만 이는 꽤 멋진 일입니다.실제로 이는 괜찮습니다. 구현 클래스에 대한 테스트를 인터페이스별로 그룹화하기 때문에 유리할 수도 있습니다.그러나 프레임워크가 더 똑똑해지면 좋을 것입니다.속성을 사용하여 클래스를 인터페이스에 대한 '공식' 테스트 클래스로 표시할 수 있고 프레임워크는 인터페이스를 구현하는 모든 클래스에 대해 테스트 중인 어셈블리를 검색하고 해당 테스트를 실행합니다.

그거 좋을 것 같아요.

[TestFixture] 클래스의 계층 구조는 어떻습니까?공통 테스트 코드를 기본 테스트 클래스에 넣고 자식 테스트 클래스에 상속합니다.

인터페이스나 기본 클래스 계약을 테스트할 때 테스트 프레임워크가 모든 구현자를 자동으로 찾도록 하는 것을 선호합니다.이를 통해 테스트 중인 인터페이스에 집중할 수 있으며 많은 수동 구현을 수행하지 않고도 모든 구현이 테스트된다는 것을 합리적으로 확신할 수 있습니다.

  • 을 위한 xUnit.net, 나는 유형 해석기 특정 유형의 모든 구현을 검색하기 위한 라이브러리(xUnit.net 확장은 Type Resolver 기능에 대한 얇은 래퍼이므로 다른 프레임워크에서 사용하도록 조정할 수 있습니다).
  • ~ 안에 Mb단위, 당신은 CombinatorialTest ~와 함께 UsingImplementations 매개변수의 속성.
  • 다른 프레임워크의 경우 기본 클래스 패턴 스포이크 언급하는 것이 유용할 수 있습니다.

인터페이스의 기본 사항을 테스트하는 것 외에도 각 개별 구현이 특정 요구 사항을 따르는지도 테스트해야 합니다.

저는 NUnit을 사용하지 않지만 C++ 인터페이스를 테스트했습니다.먼저 일반적인 내용이 작동하는지 확인하기 위해 기본 구현인 TestFoo 클래스를 테스트하겠습니다.그런 다음 각 인터페이스에 고유한 항목을 테스트하면 됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top