[Boostcourse] 풀스택/웹 프로그래밍(풀스택)

Spring Test { 통합 테스트, Mock, @InjectMocks, @Mock, given( ), verify( ) }

Ben의 프로그램 2023. 7. 26. 18:20
728x90
수업목표
지난 강의에 스프링 빈 컨테이너가 관리하는 빈(Bean)을 테스트하는 방법에 대해 살펴보았습니다. 빈과 빈 사이에는 다양한 관계가 있습니다. 이번 시간에는 그 관계를 목(Mock) 객체를 이용하여 끊고, 테스트하고자 하는 객체에만 집중하여 테스트하는 단위 테스트(unit test)에 대해 알아보도록 하겠습니다.   

 

통합 테스트 란?
빈들 간에는 다양한 관계를 맺고 있는 경우가 많습니다. 하나의 빈을 사용한다는 것은 관계된 빈들도 함께 동작한다는 것을 의미합니다. 하나의 빈을 테스트할 때 관련된 빈들이 모두 잘 동작하는지 테스트하는 것을 우리는 통합 테스트(integration test)라 합니다. 

 

목(Mock) 을 사용하게 되는 이유
관계된 다른 클래스와는 상관 없이 특정 빈이 가지고 있는 기능만 잘 동작하는지 확인하는 것을 우리는 단위 테스트(Unit Test)라 합니다.

MyService 라는 클래스를 이전 시간에 실습했던 내용에 추가해보겠습니다. MyService 클래스는 CalculatorService 를 필드로 가지고 있는 것을 확인할 수 있습니다. 해당 필드는 생성자를 통해서 초기화를 하는 것을 확인할 수 있습니다. 게다가 MyService 가 가지고 있는 execute( ) 메서드는 caculatorService.plus( ) 메서드를 사용합니다. 

이렇게 작성된 MyService 객체를 테스트하는 MyServiceTest 클래스는 위와 같이 작성할 수 있습니다. @RunWith(SpringJUnit4ClassRunner.class) 는 내부적으로 스프링 빈 컨테이너를 생성한다고 하였는데요. 스프링 빈 컨테이너는 빈들을 찾아 메모리에 올리게 됩니다. 그리고 나서 myService 필드에 객체를 주입하게 됩니다. 테스트 메서드는 myService.excute( )를 실행합니다. execute( ) 메서드는 내부적으로 calculatorService.plus( ) 메서드를 사용합니다. 최종적으로 execute 메서드는 5와 10을 더한 값에 2를 곱한 결과를 반환하는데요. assertEquals( ) 메서드의 코드로 비교한 상황을 보면 true 가 나올 것이라고 예상할 수 있습니다. 그런데, 만약 CalculatorService 클래스의 plus( ) 메서드에 버그가 있다면 어떻게 될까요? 이렇게 되면 위의 코드를 보면 논리적으로 아무 문제가 없지만 동작하는 것은 내가 생각한 것과는 다르게 동작하게 될 겁니다. 즉, MyService 를 테스트하려고 했지만 CalculatorService 의 버그로 인해 에러가 발생하고 있는 어처구니 없는 상황인 것이죠. 이런 문제를 해결하려면 어떻게 해야할까요? 이런 문제를 해결하는 방법 중 하나가 바로 목(Mock) 객체를 이용하는 방법입니다.

 

목(Mock) 객체 사용하기
MyService 가 사용하던 CalculatorService 를 사용하는 대신, 가짜 객체를 하나 생성하도록 하는 것입니다. 내가 원하는 동작을 하는 Mock 객체로 CalculatorService 를 사용함으로써 MyService 의 내용만 테스트를 수행할 수 있습니다. 이것을 수행하기 위해 pom.xml 파일에 다음을 추가해줍니다. 

mockito 는 "모히또"에서 따온 이름이라고 하는데요. mockito 는 오픈소스 Mock 프레임워크입니다. 테스트를 위한 가짜 객체를 쉽게 만들어 줄 수 있는 프레임워크라고 생각하면 편합니다. Mockito 프레임워크 자체만해도 다뤄야 할 양이 많아서 Mockito 에 대해 모두 다루기는 어렵습니다. 이번 시간은 Mockito 를 이용해 목 객체를 사용하는 것이 이런 것이구나 정도만 느끼는 것을 목적으로 공부해봅시다. 

@RunWith(MockitoJUnitRunner.class)
: 이 애노테이션을 보면 기존 JUnit 을 사용하는 것이 아니라 mockito 가 제공하는 JUnit 확장 클래스인 MockitoJUnitRunner 를 이용해 테스트 클래스를 실행하도록 설정하고 있습니다.

@Mock
: 위 애노테이션은 caculatorService 필드가 목 객체를 참조하도록 합
니다. 즉, 개발자가 객체를 생성하지 않아도 자동으로 객체가 생성되고 해당 필드가 초기화된다는 것을 의미합니다.

@InjectMocks
: 위 어노테이션이 붙은 필드는 목 객체를 사용하는 MyService 객체를 생성하여 초기화하라는 의미를 갖습
니다. 

given(calculatorService.plus(5, 10)).willReturn(15); 
: 위 코드에서 given( ) 은 static 메서드입니다. import 문 중에 org.mockito.BDDMockito 클래스의 static 메서드인데요. MyServiceTest 클래스의 calculatorService 는 가짜 객체입니다. 이 가짜 객체가 동작하는 방법을 규정할 수 있는 것이 given( ) 메서드입니다. calculatorService.plus(5, 10) 을 호출하면 plus 메서드가 15를 반환하도록 하라는 의미를 가집니다. 따라서 result 필드는 무조건 30을 대입받게 됩니다. 

verify(calculatorService).plus(anyInt( ), anyInt( )); 
: verify( ) 메서드는 org.mockito.Mockito 의 static 메서드입니다. anyInt( ) 메서드는 org.mockito.Matchers 의 static 메서드입니다. 위의 verify 메서드는 파라미터로 들어온 객체의 plus 메서드가 호출된 적이 있는지 검증합니다. 특히 여기서 사용된 plus( ) 메서드는 어떤 정수든지 2개를 파라미터로 넣어서 plus( ) 메서드가 호출되었는지를 검증합니다. 즉, myService.execute( ) 메서드 내부적으로 plus( ) 메서드를 호출했다면 verify( ) 메서드는 검증을 성공하게 됩니다. 만약 plus( ) 메서드가 호출된 적이 없다면 오류가 발생하게 됩니다. 

execute( ) 메서드를 위와 같이 수정한 후 테스트를 실행해보면 plus( ) 메서드가 실행된 적이 없기 때문에 다음과 같은 오류가 발생하게 됩니다. 

 

 

출처 : boostcourse 웹 프로그래밍(풀스택) 
https://www.boostcourse.org/web316/lecture/20655?isDesc=false