NUnit으로 개인 메서드를 어떻게 테스트합니까?


104

NUnit을 올바르게 사용하는 방법이 궁금합니다. 먼저 메인 프로젝트를 참조로 사용하는 별도의 테스트 프로젝트를 만들었습니다. 그러나이 경우 개인 메서드를 테스트 할 수 없습니다. 내 생각 엔 테스트 코드를 메인 코드에 포함시켜야한다고 생각 했나?! -그게 올바른 방법이 아닌 것 같습니다. (나는 테스트가 포함 된 코드를 배송한다는 생각이 싫다.)

NUnit으로 개인 메서드를 어떻게 테스트합니까?

답변:


74

일반적으로 단위 테스트는 클라이언트의 관점에서 결과가 정확하다면 구현이 중요하지 않다는 이론에 따라 클래스의 공용 인터페이스를 다룹니다.

따라서 NUnit은 비공개 멤버를 테스트하기위한 메커니즘을 제공하지 않습니다.


1
+1 저는 방금이 문제에 대해 생각해 봤습니다. 제 경우에는 개인과 공용 사이에서 발생하는 "매핑"알고리즘이 있습니다. 만약 제가 공용 단위 테스트를한다면 실제로는 통합 테스트가 될 것입니다. 이 시나리오에서는 코드의 디자인 문제에 대한 테스트 포인트를 작성하려고합니다. 이 경우 "개인"메서드를 수행하는 다른 클래스를 만들어야합니다.
andy

140
나는이 주장에 완전히 동의하지 않는다. 단위 테스트는 클래스에 관한 것이 아니라 코드 에 관한 것 입니다 . 하나의 공용 메서드가있는 클래스가 있고 공용 메서드의 결과를 생성하는 데 10 개의 개인 메서드가 사용 된 경우 각 개인 메서드가 의도 한 방식으로 작동하는지 확인할 방법이 없습니다. 올바른 개인 메서드를 올바른 방식으로 적중하는 공용 메서드에 대한 테스트 케이스를 만들 수 있습니다. 그러나 public 메서드가 절대 변경되지 않는다고 믿지 않는 한 private 메서드가 실제로 호출되었는지 여부는 알 수 없습니다. 개인 메서드는 작성자가 아닌 프로덕션 코드 사용자에게 전용입니다.
Quango

3
@Quango, 표준 테스트 설정에서 "작성자" 코드의 프로덕션 사용자입니다. 그럼에도 불구하고 디자인 문제의 냄새가 나지 않는다면 클래스를 변경하지 않고 비공개 메서드를 테스트 할 수있는 옵션이 있습니다. System.Reflection바인딩 플래그를 사용하여 비공개 메서드에 액세스하고 호출 할 수 있으므로 NUnit을 해킹하거나 자체 프레임 워크를 설정할 수 있습니다. 또는 (쉽게 생각합니다) 컴파일 타임 플래그 (#if TESTING)를 설정하여 액세스 수정자를 변경하여 기존 프레임 워크를 사용할 수 있습니다.
harpo

23
개인 메서드는 구현 세부 사항입니다. 단위 테스트의 요점은 동작 을 변경하지 않고 구현의 리팩토링을 허용 하는 것 입니다. private 메서드가 너무 복잡해져 테스트를 개별적으로 다루고 자하는 경우이 기본형을 사용할 수 있습니다. "private 메서드는 항상 별도의 클래스의 공용 (또는 내부) 메서드 일 수 있습니다." 즉, 논리 조각이 테스트 대상이 될만큼 충분히 발전된 경우 자체 테스트를 통해 자체 클래스가 될 수 있습니다.
Anders Forsgren 2013 년

6
@AndersForsgren 그러나 통합 또는 기능 테스트 와 달리 단위 테스트 의 요점은 이러한 세부 사항을 정확하게 테스트하는 것입니다. 그리고 코드 커버리지는 중요한 단위 테스트 메트릭으로 간주되므로 이론적으로 모든 논리는 "테스트 대상이 될만큼 충분히 발전된 것"입니다.
Kyle Strand

57

단위 테스트의 초점이 공용 인터페이스 여야한다는 데 동의하지만 개인 메서드도 테스트하면 코드에 대해 훨씬 더 세분화 된 인상을받을 수 있습니다. MS 테스트 프레임 워크에서는 PrivateObject 및 PrivateType을 사용하여이를 허용하지만 NUnit은 그렇지 않습니다. 대신 내가하는 일은 :

private MethodInfo GetMethod(string methodName)
{
    if (string.IsNullOrWhiteSpace(methodName))
        Assert.Fail("methodName cannot be null or whitespace");

    var method = this.objectUnderTest.GetType()
        .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

    if (method == null)
        Assert.Fail(string.Format("{0} method not found", methodName));

    return method;
}

이 방법은 테스트 가능성을 위해 캡슐화를 손상시킬 필요가 없음을 의미합니다. 비공개 정적 메서드를 테스트하려면 BindingFlags를 수정해야합니다. 위의 예는 인스턴스 메서드에 대한 것입니다.


7
이러한 작은 도우미 메서드를 테스트하면 단위 테스트에서 단위 부분이 캡처됩니다. 모두가 유틸리티 클래스에 적합한 것은 아닙니다.
Johan Larsson

12
이것이 바로 내가 필요한 것입니다. 감사! 내가 필요한 것은 비공개 필드가 제대로 설정되었는지 확인하는 것이었고, 공개 인터페이스를 통해이를 결정하는 방법을 파악하는 데 3 시간을 소비 할 필요가 없었습니다. 이것은 변덕에 리팩토링 할 수없는 기존 코드입니다. 간단한 질문이 이상주의 교리에 대답 할 수있는 방법은 ... 재미
Droj

5
이것은 실제로 질문에 대답하는 유일한 대답이므로 선택된 대답이어야합니다.
bavaza 2013

6
동의합니다. 선택한 답변은 장점이 있지만 OP의 질문이 아니라 "개인적인 방법을 테스트해야하나요"에 대한 답변입니다.
chesterbr

2
작동합니다. 감사합니다! 이것은 많은 사람들이 검색하는 것입니다. 이것은 선택된 답변이어야합니다.
Able Johnson

47

단위 테스트를 작성하는 일반적인 패턴은 공용 메서드 만 테스트하는 것입니다.

테스트하려는 개인 메서드가 많이있는 경우 일반적으로 코드를 리팩터링해야한다는 신호입니다.

이러한 메서드를 현재 살고있는 클래스에서 공개하는 것은 잘못된 것입니다. 그것은 당신이 그 클래스가 갖기를 원하는 계약을 깨뜨릴 것입니다.

헬퍼 클래스로 이동하여 공개하는 것이 옳을 수 있습니다. 이 클래스는 API에 의해 노출되지 않을 수 있습니다.

이렇게하면 테스트 코드가 공개 코드와 혼합되지 않습니다.

비슷한 문제는 개인 클래스를 테스트하는 것입니다. 어셈블리에서 내 보내지 않는 클래스. 이 경우 InternalsVisibleTo 특성을 사용하여 테스트 코드 어셈블리를 프로덕션 코드 어셈블리의 친구로 명시 적으로 만들 수 있습니다.


1
+ 1 그래! 테스트를 작성하는 동안 그 결론에 도달했습니다. 좋은 조언입니다!
andy

1
테스트하고 싶지만 API 사용자에게 노출하지 않는 비공개 메서드를 노출되지 않은 다른 클래스로 이동하고 공개로 만드는 것이 정확히 내가 찾고 있던 것입니다.
Thomas N

@morechilli 감사합니다. 전에 InternalsVisibleTo를 본 적이 없습니다. 테스트가 훨씬 쉬워졌습니다.
Andrew MacNaughton

안녕하세요. 최근에 댓글없이 일부 반대표를 받았습니다. 귀하의 생각이나 우려 사항을 설명하기 위해 몇 가지 피드백을 제공하십시오.
morechilli

6
따라서 해당 클래스에서만 필요한 매우 구체적인 작업을 수행하기 위해 클래스 내의 여러 위치에서 호출되는 private 메서드가 있고 공용 API의 일부로 노출되지 않는 경우 ... 도우미에 넣는 것입니다. 테스트를 위해 수업? 수업을 위해 공개하는 것이 좋습니다.
Daniel Macias

21

테스트중인 대상 어셈블리의 동반자 어셈블리로 테스트 어셈블리를 선언하여 프라이빗 메서드를 테스트 할 수 있습니다. 자세한 내용은 아래 링크를 참조하십시오.

http://msdn.microsoft.com/en-us/library/0tke9fxk.aspx

이것은 대부분 프로덕션 코드에서 테스트 코드를 분리하므로 유용 할 수 있습니다. 이 방법에 대한 필요성을 찾지 못했기 때문에이 방법을 직접 사용한 적이 없습니다. 코드가 어떻게 처리하는지보기 위해 테스트 환경에서 복제 할 수없는 극단적 인 테스트 케이스를 테스트하는 데 사용할 수 있다고 생각합니다.

그래도 말했듯이 개인 메서드를 테스트 할 필요는 없습니다. likley보다 더 작은 빌딩 블록으로 코드를 리팩터링하기를 원합니다. 리팩토링 할 때 도움이 될 수있는 한 가지 팁은 시스템이 관련된 도메인에 대해 생각하고이 도메인에 서식하는 '실제'개체에 대해 생각하는 것입니다. 시스템의 개체 / 클래스는 실제 개체와 직접 관련되어야하며 개체가 포함해야하는 정확한 동작을 격리하고 개체 책임을 제한 할 수 있습니다. 이것은 특정 메소드를 테스트 할 수 있도록하기보다는 논리적으로 리팩토링하고 있음을 의미합니다. 개체 동작을 테스트 할 수 있습니다.

여전히 내부 테스트의 필요성을 느낀다면 코드 한 조각에 집중하고 싶기 때문에 테스트에서 조롱을 고려할 수도 있습니다. 모킹은 객체 종속성을 주입하지만 주입 된 객체는 '실제'또는 프로덕션 객체가 아닙니다. 동작 오류를 쉽게 분리 할 수 ​​있도록 하드 코딩 된 동작이있는 더미 개체입니다. Rhino.Mocks는 기본적으로 개체를 작성하는 인기있는 무료 모의 프레임 워크입니다. TypeMock.NET (커뮤니티 에디션을 사용할 수있는 상용 제품)은 CLR 개체를 모의 할 수있는 더 강력한 프레임 워크입니다. 예를 들어 데이터베이스 앱을 테스트 할 때 SqlConnection / SqlCommand 및 Datatable 클래스를 모의하는 데 매우 유용합니다.

이 답변이 일반적으로 단위 테스트에 대해 알리고 단위 테스트에서 더 나은 결과를 얻는 데 도움이되는 더 많은 정보를 제공하기를 바랍니다.


12
비공개가 아닌 내부 메소드 를 테스트 할 수 있습니다 (NUnit 사용).
TrueWill

6

저는 개인 메서드를 테스트 할 수있는 기능을 선호합니다. xUnit이 시작되었을 때 코드가 작성된 후 기능을 테스트하기위한 것이 었습니다. 이 목적을 위해 인터페이스 테스트만으로 충분합니다.

단위 테스트는 테스트 중심 개발로 발전했습니다. 모든 방법을 테스트 할 수있는 기능은 해당 애플리케이션에 유용합니다.


5

이 질문은 고급 단계에 있지만이 작업을 수행하는 방식을 공유 할 것이라고 생각했습니다.

기본적으로 모든 단위 테스트 클래스는 해당 어셈블리의 '기본값'아래에있는 'UnitTest'네임 스페이스에 테스트중인 어셈블리에 있습니다. 각 테스트 파일은 다음과 같이 래핑됩니다.

#if DEBUG

...test code...

#endif

블록, 그리고 그것은 모두 a) 릴리스에서 배포되지 않고 b) 후프 점프없이 internal/ Friend레벨 선언을 사용할 수 있음을 의미합니다 .

이것이 제공하는 다른 것은이 질문과 더 관련이있는 partial클래스 의 사용입니다. 클래스를 사용하여 개인 메서드를 테스트하기위한 프록시를 만들 수 있습니다. 예를 들어 정수 값을 반환하는 개인 메서드와 같은 것을 테스트하는 데 사용할 수 있습니다.

public partial class TheClassBeingTested
{
    private int TheMethodToBeTested() { return -1; }
}

어셈블리의 기본 클래스 및 테스트 클래스에서 :

#if DEBUG

using NUnit.Framework;

public partial class TheClassBeingTested
{
    internal int NUnit_TheMethodToBeTested()
    {
        return TheMethodToBeTested();
    }
}

[TestFixture]
public class ClassTests
{
    [Test]
    public void TestMethod()
    {
        var tc = new TheClassBeingTested();
        Assert.That(tc.NUnit_TheMethodToBeTested(), Is.EqualTo(-1));
    }
}

#endif

개발 중에이 메서드를 사용하지 않도록해야합니다.하지만 릴리스 빌드는 곧 부주의 한 호출을 표시 할 것입니다.


4

단위 테스트의 주요 목표는 클래스의 공용 메서드를 테스트하는 것입니다. 이러한 공용 메서드는 해당 개인 메서드를 사용합니다. 단위 테스트는 공개적으로 사용 가능한 동작을 테스트합니다.


3

이것이 질문에 대한 답이 아니라 리플렉션을 사용하거나 #if #endif 문을 사용하거나 비공개 방법을 표시하는 것과 같은 솔루션이 문제를 해결하지 못하는 경우 사과합니다. 비공개 메서드를 표시하지 않는 데는 여러 가지 이유가있을 수 있습니다. 예를 들어 프로덕션 코드이고 팀이 단위 테스트를 소급해서 작성하는 경우에는 어떨까요?

내가 작업중 인 프로젝트의 경우 (슬프게도) 접근자를 사용하여 개인 메서드를 단위 테스트하는 방법이있는 것으로 보입니다.


2

개인 기능을 테스트하지 않습니다. 리플렉션을 사용하여 개인 메서드 및 속성에 들어가는 방법이 있습니다. 그러나 그것은 정말 쉬운 일이 아니며 저는이 관행을 강력히 권장하지 않습니다.

공개되지 않은 것을 테스트해서는 안됩니다.

내부 메서드와 속성이있는 경우이를 공개로 변경하거나 앱과 함께 테스트를 제공하는 것을 고려해야합니다 (실제로 문제로 보지 않는 것).

고객이 Test-Suite를 실행할 수 있고 제공 한 코드가 실제로 "작동"하는 것을 확인할 수있는 경우 (이를 통해 IP를 제공하지 않는 한) 이것이 문제라고 생각하지 않습니다. 모든 릴리스에 포함 된 것은 테스트 보고서와 코드 커버리지 보고서입니다.


일부 고객은 예산을 적게 유지하고 테스트 범위에 대한 토론을 시작합니다. 이 사람들에게 테스트를 작성하는 것은 당신이 나쁜 코더이고 자신의 기술을 신뢰하지 않기 때문이 아니라는 것을 설명하기 어렵습니다.
MrFox

1

단위 테스트 이론에서는 계약 만 테스트해야합니다. 즉, 클래스의 공개 멤버 만. 그러나 실제로 개발자는 일반적으로 내부 구성원을 테스트하려고합니다. -나쁘지 않습니다. 예, 이론에 위배되지만 실제로는 때때로 유용 할 수 있습니다.

따라서 내부 구성원을 실제로 테스트하려면 다음 접근 방식 중 하나를 사용할 수 있습니다.

  1. 회원을 공개합니다. 많은 책에서 저자들은이 접근 방식을
  2. 회원을 내부로 만들고 InternalVisibleTo를 추가 할 수 있습니다. Assebly 를
  3. 클래스 멤버를 보호하고 테스트중인 클래스에서 테스트 클래스를 상속 할 수 있습니다.

코드 예 (의사 코드) :

public class SomeClass
{
    protected int SomeMethod() {}
}
[TestFixture]
public class TestClass : SomeClass{

    protected void SomeMethod2() {}
    [Test]
    public void SomeMethodTest() { SomeMethod2(); }
}

바퀴벌레가 예제 코드 안에 숨겨져있는 것 같습니다.) NUnit의 조명기 내부 메서드는 public이어야합니다 Message: Method is not public. 그렇지 않으면 .
Gucu112 2017

@ Gucu112 감사합니다. 나는 그것을 고쳤다. 일반적으로 목표는 디자인 POV에서 비정상적인 것을 보여주는 것이기 때문에 중요하지 않습니다.
burzhuy

1

메서드를 내부적으로 보호하고 다음을 사용할 수 있습니다. assembly: InternalsVisibleTo("NAMESPACE") 테스트 네임 스페이스에 수 있습니다.

따라서 아니오! 개인 메서드에 액세스 할 수는 없지만 이것은 해결 방법입니다.


0

개인 메서드 패키지를 표시합니다. 이렇게하면 해당 메서드를 테스트 할 수있는 동안 합리적으로 비공개로 유지할 수 있습니다. 나는 사람들이 공개 인터페이스가 테스트되어야 할 유일한 인터페이스라는 말에 동의하지 않습니다. 외부 인터페이스를 통해서만 제대로 테스트 할 수없는 개인 메서드에는 종종 정말 중요한 코드가 있습니다.

따라서 올바른 코드 또는 정보 숨기기에 더 관심이 있는지 여부가 결정됩니다. 나는 패키지 가시성이 좋은 타협이라고 말하고 싶습니다. 그 방법에 액세스하려면 누군가가 자신의 클래스를 패키지에 배치해야하기 때문입니다. 그것은 그들이 정말 똑똑한 일인지에 대해 두 번 생각하게 만들 것입니다.

저는 Java 사람 btw이므로 패키지 visiblilty는 C #에서 완전히 다른 이름으로 불릴 수 있습니다. 이러한 메서드에 액세스하기 위해 두 클래스가 동일한 네임 스페이스에 있어야하는 경우라고해도 충분합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.