[C#] C #에서 개인 메서드의 단위 테스트

Visual Studio에서는 자동으로 생성 된 접근 자 클래스를 통해 개인 메서드의 단위 테스트를 수행 할 수 있습니다. 성공적으로 컴파일하는 개인용 메소드의 테스트를 작성했지만 런타임에 실패합니다. 코드와 테스트의 버전은 다음과 같습니다.

//in project MyProj
class TypeA
{
    private List<TypeB> myList = new List<TypeB>();

    private class TypeB
    {
        public TypeB()
        {
        }
    }

    public TypeA()
    {
    }

    private void MyFunc()
    {
        //processing of myList that changes state of instance
    }
}

//in project TestMyProj           
public void MyFuncTest()
{
    TypeA_Accessor target = new TypeA_Accessor();
    //following line is the one that throws exception
    target.myList.Add(new TypeA_Accessor.TypeB());
    target.MyFunc();

    //check changed state of target
}

런타임 오류는 다음과 같습니다.

Object of type System.Collections.Generic.List`1[MyProj.TypeA.TypeA_Accessor+TypeB]' cannot be converted to type 'System.Collections.Generic.List`1[MyProj.TypeA.TypeA+TypeB]'.

intellisense에 따르면 컴파일러가 TypeA_Accessor 유형이라고 생각합니다. 그러나 런타임시 유형은 TypeA이므로 목록 추가에 실패합니다.

이 오류를 막을 수있는 방법이 있습니까? 또는 다른 사람들이 할 수있는 다른 조언 (아마도 “비공개 방법을 테스트하지 않음”및 “객체 상태를 조작하는 단위 테스트가 없음”)이있을 수 있습니다.



답변

예, 비공개 메소드를 테스트하지 마십시오 …. 단위 테스트의 아이디어는 공개 ‘API’로 단위를 테스트하는 것입니다.

많은 개인 행동을 테스트 해야하는 경우 테스트하려는 클래스 내에 새로운 ‘클래스’가 숨겨져 있으며 추출하여 공용 인터페이스로 테스트합니다.

하나의 조언 / 사고 도구 ….. 어떤 방법도 사적인 것이 될 수 없다는 생각이 있습니다. 모든 메소드가 객체의 공용 인터페이스에 있어야 함을 의미합니다. 비공개로 만들어야한다고 생각되면 다른 객체에있을 가능성이 큽니다.

이 조언은 실제로는 잘 작동하지 않지만 대부분 좋은 조언이며 종종 사람들이 물건을 더 작은 물건으로 분해하도록합니다.


답변

PrivateObject 클래스를 사용할 수 있습니다

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(expectedVal, retVal);


답변

“표준 또는 모범 사례로 불리는 것은 없으며 아마도 대중적인 의견 일 것입니다.”

이 논의에서도 마찬가지입니다.

여기에 이미지 설명을 입력하십시오

그것은 모두 유닛이라고 생각하는 것에 달려 있습니다 .UNIT이 클래스라고 생각하면 공개 메소드 만 공격합니다. UNIT이 개인 메소드를 공격하는 코드 라인이라고 생각하면 죄책감을 느끼지 않습니다.

개인 메소드를 호출하려면 “PrivateObject”클래스를 사용하고 호출 메소드를 호출하십시오. “PrivateObject”사용 방법을 보여주고 비공개 메소드 테스트가 논리적인지 아닌지에 대한 심층적 인 YouTube 비디오 ( http://www.youtube.com/watch?v=Vq6Gcs9LrPQ )를 볼 수 있습니다 .


답변

여기서 또 다른 생각은 테스트를 “내부”클래스 / 메서드로 확장하여이 테스트에 대한 화이트 박스 감각을 더 많이주는 것입니다. 어셈블리에서 InternalsVisibleToAttribute를 사용하여 개별 단위 테스트 모듈에 노출 할 수 있습니다.

봉인 클래스와 함께 테스트 방법이 unittest 어셈블리에서만 볼 수 있도록 캡슐화에 접근 할 수 있습니다. 봉인 된 클래스의 보호 된 메소드는 사실상 개인용입니다.

[assembly: InternalsVisibleTo("MyCode.UnitTests")]
namespace MyCode.MyWatch
{
    #pragma warning disable CS0628 //invalid because of InternalsVisibleTo
    public sealed class MyWatch
    {
        Func<DateTime> _getNow = delegate () { return DateTime.Now; };


       //construktor for testing purposes where you "can change DateTime.Now"
       internal protected MyWatch(Func<DateTime> getNow)
       {
           _getNow = getNow;
       }

       public MyWatch()
       {
       }
   }
}

그리고 단위 테스트 :

namespace MyCode.UnitTests
{

[TestMethod]
public void TestminuteChanged()
{
    //watch for traviling in time
    DateTime baseTime = DateTime.Now;
    DateTime nowforTesting = baseTime;
    Func<DateTime> _getNowForTesting = delegate () { return nowforTesting; };

    MyWatch myWatch= new MyWatch(_getNowForTesting );
    nowforTesting = baseTime.AddMinute(1); //skip minute
    //TODO check myWatch
}

[TestMethod]
public void TestStabilityOnFebruary29()
{
    Func<DateTime> _getNowForTesting = delegate () { return new DateTime(2024, 2, 29); };
    MyWatch myWatch= new MyWatch(_getNowForTesting );
    //component does not crash in overlap year
}
}


답변

개인 메소드를 테스트하는 한 가지 방법은 리플렉션을 통하는 것입니다. 이것은 NUnit 및 XUnit에도 적용됩니다.

MyObject objUnderTest = new MyObject();
MethodInfo methodInfo = typeof(MyObject).GetMethod("SomePrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
object[] parameters = {"parameters here"};
methodInfo.Invoke(objUnderTest, parameters);


답변

Ermh … 똑같은 문제로 여기에 왔습니다 : 간단 하지만 중요한 개인 방법을 테스트하십시오 . 이 스레드를 읽은 후에는 “이 간단한 금속 조각에이 간단한 구멍을 뚫고 싶어서 품질이 사양에 맞는지 확인하고 싶다”고 표시됩니다. “좋아요, 쉽지 않습니다. 우선은, 그렇게 할 적절한 도구가없는,하지만 당신은. 당신의 정원에서 중력 파 전망대를 구축에서 내 기사를 읽을 수 http://foobar.brigther-than-einstein.org/ 첫째, 물론, 당신 고급 양자 물리학 과정에 참석해야합니다. 그러면 엄청난 양의 매우 차가운 질소가 필요합니다. 물론 아마존에서 저의 책을 볼 수 있습니다.

다시 말해…

아니요, 먼저해야합니다.

각각의 모든 방법은 전용 수도 내부 보호 공중 가지고 검증한다. 여기에 제시된 것과 같은 방해없이 이러한 테스트를 구현할 수있는 방법이 있어야합니다.

왜? 정확히 일부 기고자들이 언급 한 건축 적 언급 때문 입니다. 소프트웨어 원칙을 간단히 반복하면 약간의 오해가 사라질 수 있습니다.

이 경우 일반적인 의심은 OCP, SRP 및 항상 KIS입니다.

그러나 잠시만 기다리십시오. 모든 것을 공개적으로 제공한다는 아이디어는 정치적이지 않고 일종의 태도입니다. 그러나. 오픈 소스 커뮤니티에서도 코드에 관해서는 이것이 교리가 아닙니다. 대신, “숨김”은 특정 API에 익숙해지기 쉬운 방법입니다. 예를 들어, 새로운 시장 출시 디지털 온도계 빌딩 블록의 핵심 계산을 숨길 수 있습니다. 실제 측정 곡선 뒤의 수학을 호기심 많은 코드 리더에게 숨기지 않고 코드가 일부에 의존하는 것을 방지하기 위해, 아마도 개인적으로 내부적으로 보호 된 코드를 사용하여 자신의 아이디어를 구현하는 것을 거부 할 수 없었던 갑자기 중요한 사용자 일 것입니다.

내가 무슨 소리 야?

개인 double TranslateMeasurementIntoLinear (double actualMeasurement);

물병 자리의 시대 또는 오늘날 불려지는 것을 선포하기는 쉽지만, 센서가 1.0에서 2.0에 도달하면 Translate …의 구현은 쉽게 이해할 수있는 간단한 선형 방정식에서 변경 될 수 있습니다. 모두를 위해, 분석 등을 사용하는 매우 정교한 계산에 사용할 수 있으므로 다른 사람의 코드를 깨뜨릴 수 있습니다. 왜? KIS조차도 소프트웨어 코딩의 핵심을 이해하지 못했기 때문입니다.

이 동화를 짧게 만들려면 : 우리는 개인적인 방법을 시험 할 수있는 간단한 방법이 필요합니다 .

첫째 : 새해 복 많이 받으세요!

둘째 : 건축가 수업을 연습하십시오.

셋째 : “공개”수정자는 해결책이 아니라 종교입니다.


답변

언급되지 않은 또 다른 옵션은 테스트하려는 객체의 자식으로 단위 테스트 클래스를 만드는 것입니다. NUnit 예 :

[TestFixture]
public class UnitTests : ObjectWithPrivateMethods
{
    [Test]
    public void TestSomeProtectedMethod()
    {
        Assert.IsTrue(this.SomeProtectedMethod() == true, "Failed test, result false");
    }
}

이렇게하면 개인 및 보호 된 (그러나 상속되지 않은 개인용) 메소드를 쉽게 테스트 할 수 있으며 모든 테스트를 실제 코드와 분리하여 테스트 어셈블리를 프로덕션에 배포하지 않아도됩니다. 많은 상속 된 객체에서 개인용 메서드를 보호 된 메서드로 전환하는 것이 허용되며 이는 매우 간단한 변경입니다.

하나…

이것은 숨겨진 방법을 테스트하는 방법의 문제를 해결하기위한 흥미로운 접근법이지만, 이것이 모든 경우에 문제에 대한 올바른 해결책이라고 주장 할 것입니다. 내부적으로 객체를 테스트하는 것이 조금 이상해 보입니다.이 접근법이 당신에게 영향을 줄 몇 가지 시나리오가있을 수 있습니다. (예를 들어, 불변의 객체는 일부 테스트를 정말 어렵게 만들 수 있습니다).

이 접근 방식을 언급하는 동안 이것은 합법적 인 솔루션보다 더 브레인 스토밍 된 제안이라고 제안합니다. 소금 한알과 함께 섭취하십시오.

편집 : 나는 이것을 분명히 나쁜 생각으로 묘사하기 때문에 사람들 이이 답변을 투표하는 것이 정말 유쾌하다는 것을 알았습니다. 사람들이 저에게 동의한다는 의미입니까? 나 진짜 혼란 스럽다…..