Junit에서 지원하는 일반적인 유닛테스트 방법은 순차적으로 테스트 진행이됩니다.

동시성 처리에서 이러한 방법은 한계가 있습니다. 예를들어 어떠한 메시지를 보내고나서

그 메시지를 검증 하기위해서 액터 작동방식을 동기적으로 값을 반환하는 방식으로 사용해야하며

비동기적 처리 원칙에 어긋나게 되며 순차적 검증방식에서 이것을 확인하기가 어렵습니다.

그래서 AKKA Testing 툴킷은 기존 모듈을 비동기적으로 처리하되, 메시지 큐를 검사하여 메시지가

올바르게 전송이되었나를 검증하게 되며 결정적으로 테스트를 위한 큐처리 시스템을 이용하게됩니다.

이러한 유닛 테스트방법이 익숙해진다고 하면, AKKA의 메시지 시스템을 학습하기에도 좋은 도구가될것입니다.


사용준비-메이븐

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			 <groupId>com.typesafe.akka</groupId>
			 <artifactId>akka-testkit_2.12</artifactId>
			 <version>${akka.version}</version>
			 <scope>test</scope>
		</dependency>		

spring test kit 역시 유닛테스를위한 라이브러리이며

akka test kit도 유사하게 설정하여 Junit에서 사용가능합니다.


SpringBoot에서 Actor 테스트 전략

유닛테스트는 서비스코드에 실제 사용되는 액터를 테스트해야합니다.

그리고 TestBase는 Spring Application의 초기화과정을 포함하며

여기 아티클은 Spring Boot에서 AKKA를 사용하는 방법에서 시작하였기에

http://wiki.webnori.com/display/AKKA/Spring+Boot+With+AKKA

순서상 SpringBoot에  AKKA를 내장하여야 합니다.


테스트 코드작성

import akka.testkit.javadsl.TestKit;


@RunWith(SpringRunner.class)
@SpringBootTest
public class CachedbApplicationTests {	
	@Autowired
	ApplicationContext context;
	
	@Test
	public void contextLoads() {
		ActorSystem system = context.getBean(ActorSystem.class);
		SpringExtension ext = context.getBean(SpringExtension.class);		
		actorTest2(system,ext);
	}
	
	protected void actorTest2(ActorSystem system,SpringExtension ext) {
		ActorRef testActor = system.actorOf(ext.props("testActor"),"service1");		
	    new TestKit(system) {{			
			testActor.tell( "hi", getRef() );
		      // await the correct response
		    expectMsg(java.time.Duration.ofSeconds(1), "너의 메시지에 응답을함");				    	
	    }};		
	}
}
	protected void actorTest3(ActorSystem system,SpringExtension ext) {
	    new TestKit(system) {{
	    	final ActorRef testActor = system.actorOf( ext.props("testActor"),"test2");	    	
	    	final TestKit probe = new TestKit(system);	//추가 테스트 객체생성..	    	
		    within(java.time.Duration.ofSeconds(3), () -> {
		    	testActor.tell( "hi", probe.getRef() );
		    	//메시지가 오기를 기다림
		    	awaitCond(probe::msgAvailable);
		    	//메시지검사
		    	probe.expectMsg(java.time.Duration.ofSeconds(0), "너의 메시지에 응답을함");
		    	return null;
		    });
	    }};		
	}
	
	protected void actorTest4(ActorSystem system,SpringExtension ext) {
	    final TestActorRef<TestActor> testB = TestActorRef.create(system, ext.props("testActor") , "test3");
	    final CompletableFuture<Object> future = PatternsCS.ask(testB, "hi", 3000).toCompletableFuture();
	    assertTrue(future.isDone());
	    try {
			assertEquals("너의 메시지에 응답을함", future.get() );
		} catch (InterruptedException | ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}


full src : http://git.webnori.com/projects/WEBF/repos/spring_cachedb/browse

분석:  Spring의 요소가 다 로드되고나서(contextLoads()) 다음 유닛테스트가 수행됩니다. - 이것은 SpringBoot의 테스트요소이며

Akka테스트를 포함시켰습니다.  이전Part에서 testActor에 인사를 하면 '너의 메시지에 응답을함' 이라고 반응하는

액터를 이미 생성을 하였습니다.  TestKit은 메시지를 전달받을수 있으며 expectMsg 라는 검사기를 통해 

비동기의 흐름을 끊지 않고  메시지처리에대한 유닛테스트기를 작성할수 있습니다.

또한 JAVA8의 새로운 비동기 기능인 CompletableFuture<> 활용도가능합니다.



테스트 컨셉

만약 이러한 검사기가 없다고하면, 액터에게 메시지 전송후 그것을 기다려야하는 코드를 작성해야 했을것입니다.

expectMsg 테스트컨셉은 메시지를 기다린다기보다, 1초이내에 해당메시지가 들어오게되면 테스트 성공이란 의미이며이것은 0.01 이내에 완료될수도 있습니다.

완료후 메시지큐를 비우기때문에 순차적인 컨셉으로 다시 테스트진행이가능합니다. 그결과를 기다려야하는 RestAPI테스트방식과는 다르다란점을 인식해야하며

이것은 순차검사와 비동기처리라는 컨셉을 적절하게 활용하는  좋은 아이디어입니다.


공식문서 : https://doc.akka.io/docs/akka/2.5/testing.html



  • No labels
Write a comment…