1. Mocking ?
Mocking은 소프트웨어 개발에서 테스트를 위해 가짜 객체를 생성하는 기술이다. 테스트에서 Mock 객체는 실제 객체를 대신하는 가짜 객체로 사용되며, 실제 객체를 호출하거나 상호작용할 필요 없이 테스트 코드에서 사용된다. Mock객체는 일반적으로 목적에 맞게 구현된 가짜 객체로, 실제 객체의 인터페이스를 구현하거나, 또는 해당 인터페이스를 상속받아 구현할 수 있다. Mocking은 일반적으로 단위 테스트(Unit Testing)에서 사용되며, 의존성이 있는 객체를 테스트하는 데 매우 유용하다. 또한, Mocking을 통해 복잡한 시스템을 테스트하거나 외부 서비스와의 상호작용을 테스트할 수 있다.
2. Mockito ?
Mockito는 자바에서 Mocking을 위한 오픈소스 프레임워크이다. Mockito를 사용하면, 테스트 코드에서 가짜 객체(Mock Object)를 만들고, 실제 객체의 동작을 대신하여 테스트를 수행할 수 있다. Mockito는 자바의 표준 테스트 프레임워크인 JUnit과 함께 사용될 수 있다.
3. Stub ?
Stubbing은 테스트 더블의 일종으로, 특정 메서드 호출 시 반환될 값을 미리 지정하는 것이다. 이를 통해, 테스트 중에 호출되는 메서드의 반환값을 조작하여 특정 상황에서 원하는 동작을 수행하도록 만들 수 있다.
LinkedList mockedList = mock(LinkedList.class);
// stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(0)); //first
System.out.println(mockedList.get(1)); // exception
System.out.println(mockedList.get(999)); // null
verify(mockedList).get(0);
이 코드는 LinkedList의 Mock 객체를 만들고, stubbing을 통해, get(0) 메소드를 호출했을 때 "first"를 리턴하도록, get(1) 메소드를 호출했을 때는 RuntimeException을 발생하도록 지정하였다. 이렇게 stubbing된 Mock 객체를 이용하여, get(0), get(1), get(999) 메소드를 각각 호출하고, 결과를 출력하였다. 그 다음으로는 verify 메소드를 사용하여, get(0) 메소드가 호출되었는지를 검증한다.
verify 메소드는 기본적으로는 호출 여부만을 검증하므로, get(0) 메소드의 반환값이 "first"인지에 대해서는 검증하지 않는다. 이는, get(0) 메소드의 반환값이 테스트에서 중요하지 않은 경우가 많기 때문이다. 만약 get(0) 메소드의 반환값이 중요한 경우, 이를 검증하기 위해 assertEquals("first", mockedList.get(0))와 같이 추가적인 코드를 작성해야 한다. 사실상 테스트에서 get(0) 메소드의 반환값이 중요하지 않은 경우에는, verify 메소드를 사용하여 검증하는 것이 불필요하다.
기본적으로, 리턴값이 있는 모든 메서드에 대해, Mock 객체는 null, 기본 자료형/래퍼 클래스 값 또는 적절한 빈 컬렉션 중 하나를 반환한다.예를 들어 int와 Integer에 대해서는 0, boolean/Boolean에 대해서는 false을 반환한다. 한 번 스텁이 되면, 메서드는 호출 횟수에 관계없이 항상 스텁된 값을 반환한다.
4. Argument matcher ?
argument matcher는 메소드 호출 시 인자로 전달된 값을 검증하거나 스텁하기 위해 사용되는 기능이다. argument matcher는 anyInt(), anyString() 등이 존재하는데, 이러한 matcher는 모든 값 또는 특정 조건을 충족하는 값과 일치하는 인자를 허용한다.
when(mockedList.get(anyInt())).thenReturn("element");
System.out.println(mockedList.get(999));
verify(mockedList).get(anyInt());
위의 코드를 보면 첫 번째 스텁은, mockedList의 get 메소드에 anyInt() 인자 매처(argument matcher)를 사용하여 호출 시 "element" 값을 반환하도록 정의하였다. 이후 get 메서드로 999를 호출한 결과를 출력한다. 또한 mockedList에서 get 메소드가 anyInt() 인자 매처로 한 번 호출되었는지 검증한다.
verify(mock).someMethod(anyInt(), anyString(), eq("third argument")); // (O)
verify(mock).someMethod(anyInt(), anyString(), "third argument"); // (X)
verify() 메서드에서 인자로 전달되는 메서드의 매개변수 중, Matcher를 사용하는 경우에는 모든 매개변수가 Matcher로 전달되어야 한다. 따라서 eq() 메서드로 문자열 "third argument"를 Matcher로 감싸주어야 한다. 만약 "third argument"를 그대로 전달하면 Matcher가 아닌 문자열로 인식하여 검증이 실패하게 된다.
eq()메서드는 Matcher 중 하나로, 인자로 전달된 값과 동등한 값을 가지는 인자를 받았는지 검증할 때 사용한다. verify() 메서드를 사용해 특정 메서드 호출을 검증할 때, 매개변수로 eq() 매처를 사용하여 특정 값을 받았는지를 검증할 수 있다.
List mockedList = mock(List.class);
// "second"를 인자로 받았는지 검증
verify(mockedList).add(eq("second"));
사실 상 위의 코드에서는 second 문자열을 eq()로 감싸지 않아도 된다. 문제가 되는것은 verify() 메서드에서 인자로 전달되는 메서드의 매개변수 중에 모두가 문자열이거나, 모두가 Matcher여야 한다는 것이다!
5. Verify (Most, Least ...)
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atMostOnce()).add("once");
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("three times");
verify(mockedList, atMost(5)).add("three times");
위 코드는 mockedList 객체의 메서드 호출 여부와 호출 횟수를 검증하는 예제이다. mockedList에 "once", "twice", "three times"라는 세 가지 문자열을 추가하고, 검증을 수행한다.
- verify(mockedList).add("once") : mockedList에 "once"라는 문자열이 한 번 추가되었는지를 검증한다.
- verify(mockedList, times(1)).add("once") : 위와 동일한 검증을 수행하지만, times(1)을 사용하여 명시적으로 한 번 호출되었는지 검증
- verify(mockedList, never()).add("never happened") : "never happened"라는 문자열이 추가되지 않았는지 검증한다.
- verify(mockedList, atMostOnce()).add("once") : "once"라는 문자열이 최대 한 번 추가되었는지 검증
- verify(mockedList, atLeastOnce()).add("three times") : "three times"라는 문자열이 적어도 한 번 이상 추가되었는지 검증
- verify(mockedList, atLeast(2)).add("three times") : "three times"라는 문자열이 최소 두 번 추가되었는지 검증
- verify(mockedList, atMost(5)).add("three times") : "three times"라는 문자열이 최대 다섯 번 추가되었는지 검증합니다.
6. Order
// A
List singleMock = mock(List.class);
singleMock.add("was added first");
singleMock.add("was added second");
InOrder inOrder = inOrder(singleMock);
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
firstMock.add("was called first");
secondMock.add("was called second");
InOrder inOrder = inOrder(firstMock, secondMock);
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
이 코드는 Mockito의 InOrder 클래스를 사용하여 Mock 객체들의 호출 순서를 검증하는 방법을 보여준다.
A ) 먼저 List 클래스의 Mock 객체(mock(List.class))를 생성하고, 이를 사용하여 add() 메서드를 호출한다. 이후 InOrder 객체를 생성하고, verify() 메서드를 사용하여 add("was added first")와 add("was added second") 메서드가 순서대로 호출되었는지 검증한다.
B) List 클래스의 Mock 객체 2개(mock(List.class) 호출 두 번)를 생성하고, 각 객체의 add() 메서드를 호출한다. 그리고 InOrder 객체를 생성하고, verify() 메서드를 사용하여 첫 번째 목 객체의 add("was called first") 메서드 호출이 먼저, 두 번째 목 객체의 add("was called second") 메서드 호출이 그 다음에 이루어졌는지 검증한다.
'Spring' 카테고리의 다른 글
[Spring Boot] 소셜로그인 구현하기 - 카카오편 (카카오, 구글, JWT ...) (0) | 2022.12.09 |
---|---|
JDBC Template 공부하기1 (query, queryForObject, update) (0) | 2022.09.28 |
댓글