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

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

OOP가 반환하는 값을 블락킹하여 검사하것이 아닌, 다양한 경로로 흘러가는 비동기 메시징을 검사하는 새로운 패턴을 알아보겠습니다.

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

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

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


Akka.net의 기본 유닛테스트 모듈이(메인개발자가 유지해주는) Xunit이며 Nunit 지원이 다소 늦어 , Xunit으로 전환하였습니다. 

Xunit 전환 : 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 : 함수단위로 검증 코드를 작성합니다.


샘플코드에서 테스트 케이스를 영어로 모두 작성하는것에 어려움이 있어 한글로 모두 변경하였습니다.


메시징을 검사하는 여러 종류의 유닛테스트들..( vs 테스트 탐색기 : 마침표를 찍으면 탐색기에 인식이 안되거나 링크가 작동안되니 주의 )


테스트 수행

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

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


테스트 컨셉 살펴보기

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

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

여기서 probe는 관찰자의 역활을 하면서, 기존 메시징 처리를 블락을 시키지않고 계속 진행을 합니다.

이러한 테스트 아키텍처는 AKKA가 아니여도 테스트 툴킷을 이해함으로 비동기 방식의 테스트 스펙의 아이디어를 참고할수 있습니다.


TDD/BDD에 대한 주제는 생략하였으며...

다음 링크를 통해  여기서 언급하지 않은 기초정보를 더 알수 있습니다.

BDD (Behaviour-Driven Development)에 대한 간략한 정리


참고링크

  • No labels