You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 13 Next »

언어와 상관없이 모던한 개발 프레임워크 템플릿들은 유닛테스트를 기본적으로 포함하고 있습니다.

자신이 작성한 서비스 코드에 대한 유닛테스트가 없다고하면 , Nunit/xUnit 중 적합한 유닛테스트를 먼저 선택을 해야하며 

단순하게,작성한  OOP가 반환하는 값을 검사하는 유닛테스트의 수준을 넘어

도메인 로직중에 발생하는 실시간 메시지에대한 유닛테스트기를 어떻게 검증을 할것인가?

메시징 유닛테스트를 조금더 우아하게 할수있는 방법을 살펴보겠습니다 .

Actor Test Source : https://github.com/psmon/AkkaForNetCore/tree/master/AkkaNetCoreTest/Actors


Akka.net의 기본 유닛테스트 모듈이(메인개발자가 유지해주는) Xunit이여서, 유닛테스트 툴을 전환하였습니다. (이문서는 Nunit기준 설명됨)

전환코드 : https://github.com/psmon/AkkaForNetCore/commit/71fd571fdf796d2cab1a8196a9ed505ff52a6cf4


준비하기

NUnit 테스트 프로젝트 종속성
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Akka.Cluster" Version="1.3.17" />
    <PackageReference Include="Akka.TestKit" Version="1.3.17" />
    <PackageReference Include="Akka.TestKit.NUnit3" Version="1.3.8" />
    <PackageReference Include="nunit" Version="3.12.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\AkkaNetCore\AkkaNetCore.csproj" />
  </ItemGroup>

</Project>

유닛 테스트 구성은 Nunit Test 와 메시지 유닛 테스트를 위해 Akka.TestKit.Nunit 을 조합하였습니다.


디렉토리 구조

유닛테스트를 위한 올바른 디렉토리 구조

만약 자신의 프로젝트 소스가 루트에서부터 시작한다고 하면,  뎁스 수정을 권장드립니다.

그러한 디렉토리구조는 이 프로젝트는 앞으로 유닛테스트가 필요없음을 명시하는 방법입니다.

일반적으로 프로젝트가 유닛테스트를 포함하고 싶다고 하면

-프로젝트명

-프젝트트명Test

위와같은 디렉토리 구조를 가지고 됩니다. ( 자바의 경우 Src하위에 UnitTest를 포함하며 계층이 약간 다릅니다.)

유닛 테스트 작성하기

서비스 코드

using System;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Event;

namespace AkkaNetCore.Actors
{
    public class CashGateActor : ReceiveActor
    {
        private readonly ILoggingAdapter logger = Context.GetLogger();
        private readonly string id;
        private Random rnd;

        public CashGateActor(int delay)
        {
            rnd = new Random();
            id = Guid.NewGuid().ToString();
            logger.Info($"현금정산게이트 액터 생성:{id} {delay}");

            ReceiveAsync<string>(async msg =>
            {
                //현금정산에 걸리는시간 1~10초, 0일때는 랜덤, 값이 주어질땐 주어진만큼                 
                int auto_delay = delay==0 ? rnd.Next(1000, 10000) : delay;
                await Task.Delay(auto_delay);
                logger.Info($"{msg}-{auto_delay}");
                Sender.Tell($"정산완료 통과하세요");
            });
        }        
    }
}

앞장 성능 확장을 위한 톨게이트 모델에서 현금정산소 Actor를 이용하여 메시지에대한 유닛테스트를 작성해보겠습니다.

메시지를 받으면 지정된 지연시간만큼(for test) 응답을 하는 심플한 코드입니다. -모듈작성때부터 유닛테스트 가능한 방법으로 작성이되면, 유닛테스트 이용과는 별개로 테스트가능한 모듈은 사용성도 높아지게되며 결국 코드품질이 높아지게됩니다.


유닛테스트

테스트 작성

using System;
using Akka.Actor;
using Akka.TestKit;
using Akka.TestKit.NUnit3;
using AkkaNetCore.Actors;
using NUnit.Framework;

namespace AkkaNetCoreTest.Actors
{
    class ActorBaseTest : TestKit
    {
        TestProbe probe;

        [SetUp]
        public void Setup()
        {
            probe = this.CreateTestProbe();
        }
        
        [TestCase(100, 300)]
        public void Actor_should_respond_within_max_allowable_time(int delay, int cutoff)
        {
            var cashGate = Sys.ActorOf(Props.Create(() => new CashGateActor(delay)));
            // sets a maximum allowable time for entire block to finish
            Within(TimeSpan.FromMilliseconds(cutoff), () =>
            {
                cashGate.Tell("정산게이트 통과요청");
                ExpectMsg("정산완료 통과하세요");
            });
        }
    }
}

유닛 테스트 작성의 큰흐름은

  • Setup : 객체 테스트에 필요한 의존 요소를 셋팅합니다.  -DI(의존성주입)기능
  • TestCase : 함수단위로 검증 코드를 작성합니다.

테스트 수행

이렇게 작성되어진 유닛테스트는 테스트 탐색기를 통해 특정 테스트만 수행할수도 있고 전체를 수행할수도 있습니다.

비동기 처리메시지를 액터와 연동함으로 비동기 메시징에 대한 처리를 유연하게 함과 동시에 심플하고 강력한 방법으로 유닛테스트기를 작성할수가 있습니다.


테스트 컨셉 살펴보기

서비스 큐에 값을 넣고, 블락을 시켜 검사하는 방법이 아닌

서비스 큐는, 블락없이 계속 작동을 시키면서 , 테스트 Probe에 쌓인 결과값을 하나씩 꺼내면서 검사를 수행할수 있습니다. 



참고링크

  • No labels