첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

1. 설치

 

아래 링크에서 비주얼 스튜디오 확장을 다운로드 받아 설치한다.

https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.TestGeneratorNUnitextension-18371

 

728x90

 

2. 작성 규칙

 

2.1 구성 규칙

  • 각각 프로젝트에 대해 1:1로 테스트 프로젝트를 작성한다.
  • 각각의 public 클래스에 대해 1:1로 테스트 클래스를 작성한다.
  • 메소드가 없는 모델 클래스의 경우 별도로 테스트 클래스를 작성하지 않고 기능 클래스에 의해 같이 테스트되도록 한다.
  • 각각의 public 메소드에 대해 최소 1개 이상의 테스트 메소드를 작성한다.
  • public 메소드가 아닌 경우 public 메소드에 의해 같이 테스트되도록 작성한다.

 

300x250

 

2.2 명명 규칙

 

프로젝트명 및 어셈블리명 : 테스트하려는 프로젝트에 .Test를 추가한다.

TestProject.Common.Test

 

클래스명 : 테스트하려는 클래스명에 Test 접미어를 추가한다.

LogManagerTest

 

메소드명 : 테스트하려는 메소드명에 Test 접미어를 추가한다.

WriteLogTest

 

▶ 만약 하나의 메소드에 대해 여러 테스트 케이스를 적용할 경우, 메소드명의 접미어로 언더스코어(_)와 [설명]을 추가한다.

WriteLogTest_Success
WriteLogTest_Error

 

반응형

 

 

3. 작성 가이드

 

예제 기준으로 작성 방법을 설명한다.

 

3.1 테스트 클래스 자동 생성

 

Visual Studio 편집 창에서 테스트 클래스를 생성하려는 대상 클래스 내부에서 마우스 오른쪽 버튼을 클릭해 컨텍스트 메뉴를 열고 [단위 테스트 만들기] 메뉴를 선택한다.

 

 

[단위 테스트 만들기] 대화 상자에서 아래와 같이 선택한다.

 

※ 한번 설정하면 Visual Studio를 다시 열기 전까지 설정이 유지된다.

 

 

[확인] 버튼을 누르면 다음과 같이 자동으로 테스트 클래스가 생성되고, 테스트 프로젝트가 없는 경우 프로젝트까지 자동 생성된다.

using NUnit.Framework;

namespace TestProject.Test
{
    [TestFixture]
    public class SimpleAuthenticatorTest
    {
        [Test]
        public void AuthenticateTest()
        {
            Assert.Fail();
        }
    }
}

 

 

3.2 기본 예제

 

아래는 간단한 단위 테스트 코드 작성 예제이다.

 

테스트 대상 클래스 코드

using NUnit.Framework;

namespace TestProject.Test
{
    [TestFixture]
    public class SimpleAuthenticatorTest
    {
        [Test]
        public void AuthenticateTest()
        {
            using(SimpleAuthenticator authenticator = new SimpleAuthenticator())
            {
                IUser user = authenticator.Authenticate();

                Assert.That(user     , Is.TypeOf<User>()         );
                Assert.That(user.Key , Is.EqualTo("gildong.hong"));
                Assert.That(user.Name, Is.EqualTo("홍길동")      );
            }
        }
    }
}

 

※ 위와 같이 테스트 결과를 검증할 때는 NUnit3에 추가된 Assert.That 메소드를 사용한다.

※ 가급적 NUnit2까지 사용하던 Assert.AreEqual, Assert.IsTrue 등 메소드 사용을 지양하고 확장성이 높은 Assert.That 메소드로 통일하여 사용한다.

 

3.3 Assert 사용하기

 

Assert.That은 위의 예제 외에도 다음과 같이 다양하게 사용할 수 있다.

Assert.That(value1, Is.False           );
Assert.That(value1, Is.Null            );
Assert.That(value1, Is.LessThan(value2));

 

테스트 하려는 코드가 예외를 발생시키는 경우, 다음 코드로 확인한다.

// 예외가 발생하지 않으면 테스트를 통과한다.
Assert.DoesNotThrow(() => network.IsAvailable()); 

// 지정 예외가 발생한 경우 테스트를 통과한다.
Assert.Throws<InvalidOperationException>(() => network.IsAvailable()); 

// 예외 메시지 조건을 충족하는 경우 테스트를 통과한다.
InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() => network.IsAvailable()); 

Assert.That(exception.Message, Is.EqualTo("TestException"));

 

3.4 초기화 기능 설정

 

다음과 같이 필요에 따라 테스트시 코드 초기화 및 정리 기능을 사용할 수 있다.

using NUnit.Framework;

namespace TestProject.Test
{
    [TestFixture]
    public class SimpleAuthenticatorTest
    {
        [OneTimeSetUp]
        public void InitializeAllTests()
        {
            // 모든 테스트 실행전 한번만 호출된다.
        } 

        [SetUp]
        public void InitialzeTest()
        {
            // 각각의 테스트 메소드 실행전 호출된다.
        } 

        [Test]
        public void AuthenticateTest()
        {
        }

        [TearDown]
        public void CleanupTest()
        {
            // 각각의 테스트 메소드 실행후 호출된다.
        } 

        [OneTimeTearDown]
        public void CleanupAllTests()
        {
            // 모든 테스트 실행후 한번만 호출된다.
        }
    }
}

 

3.5 TestCase 특성 사용

 

다음과 같이 TestCase 특성을 이용하여 동일한 테스트 함수를 여러번 사용할 수도 있다.

 

아래 예제에서는 테스트 사례별로 NumberTest 메소드가 4번 호출되며, NumberTest 메소드의 반환값과 TestCase 특성의 ExpectedResult 인자값을 비교하여 값이 동일한 경우만 테스트를 통과한다.

using NUnit.Framework;

namespace TestProject.Test
{
    [TestFixture]
    public class NumberTest
    {
        private int current = 0; 

        [TestCase(ExpectedResult = 1)] // 테스트 성공
        [TestCase(ExpectedResult = 2)] // 테스트 성공
        [TestCase(ExpectedResult = 3)] // 테스트 성공
        [TestCase(ExpectedResult = 5)] // 테스트 실패
        public int NumberTest() => ++current;
    } 
}

 

 

3.6 TestFixture 특성 사용

 

테스트 클래스에 TestFixture 특성을 이용해 위와 유사하게 테스트 클래스를 재활용하여 테스트를 반복 수행할 수도 있다.

https://docs.nunit.org/articles/nunit/writing-tests/attributes/testfixture.html

 

3.7 Moq 설치 및 사용

 

Moq 누겟 참조를 추가한다.

 

Moq를 사용하면 메소드나 속성이 특정 값을 리턴하거나 예외를 발생시키도록 흉내(mock)내어, 테스트 시나리오를 자동화하고 코드 커버리지를 증가시키는데 도움을 준다.

 

다음은 VPN 클라이언트를 실행하지 않고도 VPN 클라이언트가 실행된 것처럼 테스트하는 예제이다.

using NUnit.Framework;

using Moq;
using Moq.Protected;

using System.Diagnostics;

[Test]
public void IsAvailableTest_Success()
{
    string sourceLine = @"
Command arguments:

Info result:

session: code: status:
200401   1     connected
200402   3     idle

There is 1 active session(s)!
";

    Mock<OfficeVPNNetwork> mock = new Mock<OfficeVPNNetwork>();

    mock.CallBase = true; // 반드시 설정한다.

    // string 타입을 반환하는 CheckProcess(process) 메소드가 어떤 Process 인자에 대해서도
    // 항상 위에서 정의된 sourceLine을 리턴하도록 설정한다.
    // CheckProcess 메소드가 protected 메소드이기 때문에 Protected() 메소드를 사용한다.
    mock.Protected()
        .Setup<string>("CheckProcess", ItExpr.IsAny<Process>())
        .Returns(sourceLine); 

    using(OfficeVPNNetwork network = mock.Object)
    {
        Assert.That(network.IsAvailable(), Is.True);
    }
}

 

다음과 같이 특정 메소드가 예외를 발생시키도록 설정할 수도 있다.

mock.Protected()
    .Setup<IUser>("Authenticate")
    .Throws(new InvalidOperationException("RESULT001"));

 

※ 주의 : Moq를 적용할 수 있는 멤버의 접근 한정자는 최소한 protected virtual이어야 한다.

 

Mock 인스턴스를 자주 생성하는 경우, 다음과 같이 MockRepository를 이용하여 생성하여 CallBase 값을 매번 설정하는 코드를 제거할 수도 있다.

using System;

using NUnit.Framework;

using Moq;
using Moq.Protected;

namespace TestProject.Test
{
    [TestFixture]
    public class SimpleAuthenticatorTest
    {
        private MockRepository mockRepository = null;

        [OneTimeSetUp]
        public void Initialize()
        {
            this.mockRepository = new MockRepository(MockBehavior.Default)
            {
                CallBase = true
            };
        } 

        [Test]
        public void AuthenticateTest_Failover()
        {
            Mock<SimpleAuthenticaor> mock = this.mockRepository.Create<SimpleAuthenticaor>();

            mock.Protected()
                .Setup<IUser>("Authenticate")
                .Throws(new InvalidOperationException("RESULT001"));

            using(SimpleAuthenticaor authenticator = mock.Object)
            {
                authenticator.ServerType = ServerType.Normal;
                authenticator.SystemID   = "SYSTEM01";

                var user = authenticator.Authenticate();

                Assert.That(user    , Is.TypeOf<User>()               );
                Assert.That(user.Key, Is.EqualTo(Environment.UserName));
            }
        }
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요