[.net] 개인 메소드를 어떻게 단위 테스트합니까?

공개 및 개인 메소드가있는 클래스 라이브러리를 작성 중입니다. 개인 메소드를 단위 테스트 할 수 있기를 원합니다 (주로 개발하는 동안 나중에 리팩토링에 유용 할 수 있음).

이를 수행하는 올바른 방법은 무엇입니까?



답변

.net을 사용하는 경우 InternalsVisibleToAttribute를 사용해야합니다 .


답변

개인 메소드를 단위 테스트하려면 무언가 잘못되었을 수 있습니다. 단위 테스트는 (일반적으로 말해서) 클래스의 인터페이스를 테스트하기위한 것으로, 공개 (및 보호) 방법을 의미합니다. 물론이 방법에 대한 해결책을 “해킹”할 수 있지만 (방법을 공개하여도) 다음 사항을 고려할 수도 있습니다.

  1. 테스트하려는 메소드가 실제로 테스트 할 가치가있는 경우 자체 클래스로 옮기는 것이 좋습니다.
  2. 개인용 메소드를 호출하는 개인용 메소드에 테스트를 추가하여 개인용 메소드의 기능을 테스트하십시오. (평론가들이 지적했듯이, 이러한 개인용 메서드의 기능이 실제로 공용 인터페이스의 일부인 경우에만이 작업을 수행해야합니다. 실제로 사용자에게 숨겨진 기능 (예 : 단위 테스트)을 수행하는 경우 이는 아마도 좋지 않습니다).

답변

개인용 메소드를 테스트하는 것은 유용하지 않을 수 있습니다. 그러나 때로는 테스트 메소드에서 개인 메소드를 호출하고 싶습니다. 테스트 데이터 생성을위한 코드 중복을 방지하기 위해 대부분의 경우 …

Microsoft는이를 위해 두 가지 메커니즘을 제공합니다.

접근 자

  • 클래스 정의의 소스 코드로 이동
  • 수업 이름을 마우스 오른쪽 버튼으로 클릭
  • “개인 접근 자 만들기”를 선택하십시오
  • 접근자를 만들 프로젝트를 선택하십시오. => 이름이 foo_accessor 인 새 클래스가 생깁니다. 이 클래스는 컴파일하는 동안 동적으로 생성되며 모든 멤버에게 공개됩니다.

그러나 원래 클래스의 인터페이스가 변경 될 때 메커니즘이 약간 다루기 어려운 경우가 있습니다. 그래서 대부분의 경우 이것을 사용하지 마십시오.

PrivateObject 클래스
다른 방법은 Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject를 사용하는 것입니다.

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );


답변

“외부 인터페이스 테스트에만 관심이 있어야합니다”라는 철학에 동의하지 않습니다. 자동차 수리점은 바퀴가 돌아가는지 확인하기위한 테스트 만해야한다고 말하는 것과 비슷합니다. 그렇습니다. 궁극적으로 나는 외부 행동에 관심이 있지만, 개인적이고 개인적인 내부 테스트가 좀 더 구체적이고 중요합니다. 예, 리팩터링하면 일부 테스트를 변경해야 할 수도 있지만 대규모 리 팩터가 아닌 한 몇 가지만 변경하면되고 다른 (변경되지 않은) 내부 테스트가 여전히 작동한다는 사실은 리팩토링이 성공했습니다.

공개 인터페이스 만 사용하여 모든 내부 사례를 다루려고 시도 할 수 있으며 이론적으로 공개 인터페이스를 사용하여 모든 내부 방법 (또는 적어도 모든 중요한 방법)을 완전히 테스트 할 수는 있지만 달성하기 위해 머리에서야 할 수도 있습니다 이것과 공개 인터페이스를 통해 실행되는 테스트 케이스와 테스트를 위해 설계된 솔루션의 내부 부분 사이의 연결은 식별하기 어렵거나 불가능할 수 있습니다. 내부 기계가 올바르게 작동하는지 확인하는 개별 테스트는 리팩토링으로 인한 사소한 테스트 변경에 가치가 있습니다. 적어도 그것은 저의 경험이었습니다. 모든 리팩토링에 대해 테스트를 크게 변경해야하는 경우에는이 방법이 적합하지 않지만이 경우 설계를 완전히 다시 생각해야합니다.


답변

드문 경우지만 개인 함수를 테스트하고 싶었지만 일반적으로 대신 보호되도록 수정했으며 공용 래퍼 함수로 하위 클래스를 작성했습니다.

클래스:

...

protected void APrivateFunction()
{
    ...
}

...

테스트를위한 서브 클래스 :

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...


답변

더 근본적인 질문은 왜 개인 메소드를 먼저 테스트하려고하는지에 대한 것입니다. 그것은 클래스 냄새 공용 인터페이스를 통해 개인 메소드를 테스트하려고 시도하는 코드 냄새이지만, 그 메소드는 구현 세부 사항이므로 사유로 인해 비공개입니다. 공개 인터페이스의 작동 방식에 대해서는 다루지 않고 공개 인터페이스의 동작에만 관심을 가져야합니다.

공통 리팩토링을 사용하여 개인 메소드의 동작을 테스트하려면 코드를 다른 클래스로 추출 할 수 있습니다 (패키지 레벨 가시성으로 공개 API의 일부가 아닌지 확인). 그런 다음 해당 동작을 격리하여 테스트 할 수 있습니다.

리팩토링의 결과는 개인 메소드가 이제 원래 클래스의 공동 작업자가 된 별도의 클래스임을 의미합니다. 그것의 동작은 자체 단위 테스트를 통해 잘 이해 될 것입니다.

그런 다음 원래 클래스를 테스트하려고 할 때 동작을 조롱하여 공용 인터페이스의 조합 폭발과 모든 개인 메소드의 동작을 테스트하지 않고 해당 클래스의 공용 인터페이스 동작 테스트에 집중할 수 있습니다. .

나는 이것이 차를 운전하는 것과 유사한 것을 본다. 차를 운전할 때 보닛을 운전하지 않아서 엔진이 작동하고 있음을 알 수 있습니다. 나는 자동차가 제공하는 인터페이스, 즉 rev 카운터와 속도계를 사용하여 엔진이 작동하고 있음을 알 수 있습니다. 나는 가스 페달을 밟을 때 차가 실제로 움직인다는 사실에 의존합니다. 엔진을 테스트하고 싶다면 별도로 검사 할 수 있습니다. :디

물론 레거시 응용 프로그램이있는 경우 개인 메서드를 직접 테스트하는 것이 최후의 수단 일 수 있지만 더 나은 테스트를 위해 레거시 코드를 리팩터링하는 것이 좋습니다. Michael Feathers는이 주제에 관해 훌륭한 책을 썼습니다. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


답변

개인 유형, 내부 및 개인 구성원은 이유가 있기 때문에 종종 직접 엉망으로 만들고 싶지 않습니다. 그렇게하면 나중에 해당 어셈블리를 만든 사람이 개인 / 내부 구현을 유지할 것이라는 보장이 없기 때문에 나중에 중단 될 가능성이 있습니다.

그러나 때로는 컴파일되거나 타사 어셈블리를 해킹 / 탐색 할 때 개인 클래스 또는 개인 또는 내부 생성자를 사용하여 클래스를 초기화하려고했습니다. 또는 때로는 변경할 수없는 사전 컴파일 된 레거시 라이브러리를 처리 할 때 개인 메서드에 대한 테스트를 작성합니다.

따라서 AccessPrivateWrapper ( http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html) 는 C # 4.0 동적 기능 및 리플렉션을 사용하여 작업을 쉽게 수행 할 수있는 빠른 래퍼 클래스입니다.

당신은 같은 내부 / 개인 유형을 만들 수 있습니다

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();