성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>MSTest - 단위 테스트에 static/instance 유형의 private 멤버 접근 방법</h1> <p> 예를 들어, 다음과 같은 식의 코드가 있다고 가정할 때,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; namespace ClassLibrary1 { public class Class1 { <span style='color: blue; font-weight: bold'>int _waitTime = 5000;</span> Stopwatch _st; public void Start() { _st = new Stopwatch(); _st.Start(); } public bool IsTooSlow() { long elapsed = _st.ElapsedMilliseconds; if (elapsed > _waitTime) { return true; } return false; } } } </pre> <br /> 단위 테스트를 이런 식으로 작성하게 될 텐데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using ClassLibrary1; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading; namespace ClassLibrary1.Tests { [TestClass()] public class Class1Tests { Class1 _cl = new Class1(); [TestMethod()] [<a target='tab' href='https://www.sysnet.pe.kr/2/0/12749'>DataRow</a>(false, 3000)] [DataRow(true, 5016)] public void IsTooSlowTest(bool expected, int sleepTime) { _cl.Start(); Thread.Sleep(sleepTime); Assert.AreEqual(expected, _cl.IsTooSlow()); } } } </pre> <br /> 그런데, 매번 저걸 테스트할 때마다 8초씩 기다려야 하는 걸까요? 물론, Class1의 _waitTime이 꼭 5000이어야 한다면 할 수 없지만, 단위 테스트에 한해 저 값을 바꿔도 무방한 코드라면 유연함을 발휘하는 것이 좋습니다.<br /> <br /> 이런 경우 선택할 수 있는 방법이 해당 필드를 internal 접근 유형으로 바꾸고, <a target='tab' href='https://www.sysnet.pe.kr/2/0/729'>InternalsVisibleTo를 이용해 단위 테스트 프로젝트를 등록</a>하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Diagnostics; using System.Runtime.CompilerServices; <span style='color: blue; font-weight: bold'>[assembly: InternalsVisibleTo("ClassLibrary1Tests")]</span> namespace ClassLibrary1 { public class Class1 { <span style='color: blue; font-weight: bold'>internal</span> int _waitTime = 5000; // ...[생략]... } } </pre> <br /> 그럼 단위 테스트 프로젝트에서 이렇게 internal 필드를 접근할 수 있어,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [TestMethod()] [DataRow(false, <span style='color: blue; font-weight: bold'>16</span>)] [DataRow(true, <span style='color: blue; font-weight: bold'>46</span>)] public void IsTooSlowTest(bool expected, int sleepTime) { <span style='color: blue; font-weight: bold'>_cl._waitTime = 30;</span> _cl.Start(); Thread.Sleep(sleepTime); Assert.AreEqual(expected, _cl.IsTooSlow()); } </pre> <br /> 단위 테스트 시간을 절약할 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 하지만, 경우에 따라서는 매번 private 필드를 internal로 바꾸는 것이 타당하지 않을 수 있습니다. 그렇다고 단위 테스트를 위해 SetWaitTime internal 메서드를 두는 것도 부담스럽다면, 마지막으로 적용할 수 있는 방법이 바로 private 필드를 Reflection으로 접근해 보는 것입니다.<br /> <br /> 사실, .NET Framework 환경에서는 마이크로소프트가 이런 경우를 위해 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.privateobject'>PrivateObject</a>/<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.privatetype'>PrivateType</a>을 제공하고 있었습니다. 그래서 위의 예제라면 다음과 같은 식으로 사용할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading; namespace ClassLibrary1.Tests { [TestClass()] public class Class1Tests { Class1 _cl; <span style='color: blue; font-weight: bold'>PrivateObject _prvObj</span>; // instance private 접근 <span style='color: blue; font-weight: bold'>PrivateType _prvType</span>; // static private 접근 [TestInitialize] public void TestInitialize() { _cl = new Class1(); <span style='color: blue; font-weight: bold'>_prvObj = new PrivateObject(_cl);</span> <span style='color: blue; font-weight: bold'>_prvType = new PrivateType(typeof(Class1));</span> } [TestMethod()] [DataRow(false, 16)] [DataRow(true, 46)] public void IsTooSlowTest(bool expected, int sleepTime) { <span style='color: blue; font-weight: bold'>_prvObj.SetField("_waitTime", 30);</span> // 만약 정적 필드라면, // _prvType.SetStaticField("_waitTime", 30); _cl.Start(); Thread.Sleep(sleepTime); Assert.AreEqual(expected, _cl.IsTooSlow()); } } } </pre> <br /> 아쉽게도 PrivateObject/PrivateType은 .NET Core/5+ 환경의 단위 테스트 프로젝트에서는 더 이상 제공하지 않고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PrivateObject and PrivateType are not available for a project targeting netcorapp2.0 #366 ; <a target='tab' href='https://github.com/Microsoft/testfx/issues/366'>https://github.com/Microsoft/testfx/issues/366</a> </pre> <br /> 어쩔 수 없습니다. 직접 만들든가 해야 하는데, 그래도 위의 덧글을 보면 마이크로소프트 측의 구현 코드 위치와 함께,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > testfx/src/TestFramework/Extension.Desktop/ ; <a target='tab' href='https://github.com/microsoft/testfx/tree/664ac7c2ac9dbfbee9d2a0ef560cfd72449dfe34/src/TestFramework/Extension.Desktop'>https://github.com/microsoft/testfx/tree/664ac7c2ac9dbfbee9d2a0ef560cfd72449dfe34/src/TestFramework/Extension.Desktop</a> </pre> <br /> 아예 zip 파일로 제공해 주고 있습니다. ^^ (첨부 파일의 프로젝트에도 PrivateObjectPrivateType.cs 파일을 포함하고 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PrivateObjectPrivateType.zip ; <a target='tab' href='https://github.com/microsoft/testfx/files/4132970/PrivateObjectPrivateType.zip'>https://github.com/microsoft/testfx/files/4132970/PrivateObjectPrivateType.zip</a> </pre> <br /> 따라서, PrivateObjectPrivateType.cs을 그대로 .NET Core/5+ 단위 테스트 프로젝트에 복사해 주고 이전 PrivateObject/PrivateType 사용 코드를 그대로 활용할 수 있습니다.<br /> <br /> 참고로, 다음의 편의성 메서드도 있으니 참고하시고.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [PrivateObject] GetArrayElement GetField GetFieldOrProperty GetProperty Invoke SetArrayElement SetField SetFieldOrProperty SetProperty [PrivateType] GetStaticArrayElement GetStaticField GetStaticProperty InvokeStatic SetStaticArrayElement SetStaticField SetStaticProperty </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그 외 자잘한 거 2개 정도 더 정리해 볼까요? ^^<br /> <br /> 해당 메서드에서 예외가 발생할 수 있다면 이렇게 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute'>ExpectedException 특성</a>을 지정할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [TestMethod] <span style='color: blue; font-weight: bold'>[ExpectedException(typeof(FileNotFoundException))]</span> public void TestException() { File.ReadAllText("test.dat"); } </pre> <br /> 그럼, FileNotFoundException 예외가 발생해도 기대했던 동작이기 때문에 위의 단위 테스트는 실패로 평가되지 않습니다.<br /> <br /> 마지막으로, 자신만의 메타데이터를 <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting.testpropertyattribute'>TestProperty</a>를 이용해 설정하는 것이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [TestMethod] <span style='color: blue; font-weight: bold'>[TestProperty("Author", "kevin")]</span> public void ExampleTest() { // Test logic } </pre> <br /> 위와 같은 경우, "Test Explorer" 창에서 "Author [kevin]"이라는 별도의 출력 항목을 "Traits" 칼럼에서 확인할 수 있습니다. <br /> <br /> <img alt='testprop_attr_1.png' src='/SysWebRes/bbs/testprop_attr_1.png' /><br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1834&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
6281
(왼쪽의 숫자를 입력해야 합니다.)