Open Mvitimin opened 6 months ago
https://www.baeldung.com/mockito-series
https://www.baeldung.com/mockito-annotations
Captor란 : 매개변수 인자 검증
@Captor
ArgumentCaptor<List<Integer>> listArgumentCaptor;
@Test
void test() {
List<Integer> numbers = new LinkedList<>();
machine.addNumbers(numbers);
verify(twoInserter).insertNumber(listArgumentCaptor.capture());
List<Integer> argumentNumbers = listArgumentCaptor.getValue();
assertEquals(1, argumentNumbers.get(0));
}
https://www.baeldung.com/bdd-mockito
public class PhoneBookService {
private PhoneBookRepository phoneBookRepository;
public void register(String name, String phone) {
if(!name.isEmpty() && !phone.isEmpty()
&& !phoneBookRepository.contains(name)) {
phoneBookRepository.insert(name, phone);
}
}
public String search(String name) {
if(!name.isEmpty() && phoneBookRepository.contains(name)) {
return phoneBookRepository.getPhoneNumberByContactName(name);
}
return null;
}
}
// Let’s look at an example of a test body using traditional Mockito:
when(phoneBookRepository.contains(momContactName))
.thenReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
verify(phoneBookRepository)
.insert(momContactName, momPhoneNumber);
// Let’s see how that compares to BDDMockito:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(momContactName, momPhoneNumber);
then(phoneBookRepository)
.should()
.insert(momContactName, momPhoneNumber);
// Using BDDMockito, we could easily configure Mockito to return a fixed result whenever our mock object target method is invoked:
given(phoneBookRepository.contains(momContactName))
.willReturn(false);
phoneBookService.register(xContactName, "");
then(phoneBookRepository)
.should(never())
.insert(momContactName, momPhoneNumber);
given(phoneBookRepository.contains(momContactName))
.willReturn(true);
given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
.will((InvocationOnMock invocation) ->
invocation.getArgument(0).equals(momContactName)
? momPhoneNumber
: null);
phoneBookService.search(momContactName);
then(phoneBookRepository)
.should()
.getPhoneNumberByContactName(momContactName);
given(phoneBookRepository.contains(xContactName))
.willReturn(false);
willThrow(new RuntimeException())
.given(phoneBookRepository)
.insert(any(String.class), eq(tooLongPhoneNumber));
try {
phoneBookService.register(xContactName, tooLongPhoneNumber);
fail("Should throw exception");
} catch (RuntimeException ex) { }
then(phoneBookRepository)
.should(never())
.insert(momContactName, tooLongPhoneNumber);
https://www.baeldung.com/mockito-argument-matchers
doReturn("Flower").when(flowerService).analyze("poppy");
when(flowerService.analyze(anyString())).thenReturn("Flower");
// To fix this and keep the String name “poppy” as desired, we’ll use eq matcher:
when(flowerService.isABigFlower(eq("poppy"), anyInt())).thenReturn(true);
flowerController.isAFlower("poppy");
String orMatcher = or(eq("poppy"), endsWith("y"));
assertThrows(InvalidUseOfMatchersException.class, () -> verify(flowerService).analyze(orMatcher));
Copy
The way we’d implement the above code is:
verify(flowerService).analyze(or(eq("poppy"), endsWith("y")));
https://www.baeldung.com/mockito-argumentcaptor
public class EmailService {
private DeliveryPlatform platform;
public EmailService(DeliveryPlatform platform) {
this.platform = platform;
}
public void send(String to, String subject, String body, boolean html) {
Format format = Format.TEXT_ONLY;
if (html) {
format = Format.HTML;
}
Email email = new Email(to, subject, body);
email.setFormat(format);
platform.deliver(email);
}
...
}
@ExtendWith(MockitoExtension.class)
class EmailServiceUnitTest {
@Mock
DeliveryPlatform platform;
@InjectMocks
EmailService emailService;
...
}
@Test
void whenDoesSupportHtml_expectHTMLEmailFormat() {
String to = "info@baeldung.com";
String subject = "Using ArgumentCaptor";
String body = "Hey, let'use ArgumentCaptor";
emailService.send(to, subject, body, true);
verify(platform).deliver(emailCaptor.capture());
Email value = emailCaptor.getValue();
assertThat(value.getFormat()).isEqualTo(Format.HTML);
}
Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
when(platform.authenticate(eq(credentials))).thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
// Here we use eq(credentials) to specify when the mock should return an object.
// Next, consider the same test using an ArgumentCaptor instead:
Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
when(platform.authenticate(credentialsCaptor.capture())).thenReturn(AuthenticationStatus.AUTHENTICATED);
assertTrue(emailService.authenticatedSuccessfully(credentials));
assertEquals(credentials, credentialsCaptor.getValue());
https://www.baeldung.com/junit https://www.baeldung.com/?s=Junit
https://www.baeldung.com/junit5-assertall-vs-multiple-assertions
User user = new User("baeldung", "support@baeldung.com", false);
assertAll(
"Grouped Assertions of User",
() -> assertEquals("admin", user.getUsername(), "Username should be admin"),
() -> assertEquals("admin@baeldung.com", user.getEmail(), "Email should be admin@baeldung.com"),
() -> assertTrue(user.getActivated(), "User should be activated")
);
https://www.baeldung.com/java-assert-equality-no-equals
Person expected = new Person(1L, "Jane", "Doe");
Address address1 = new Address(1L, "New York", "Sesame Street", "United States");
expected.setAddress(address1);
Person actual = new Person(1L, "Jane", "Doe");
Address address2 = new Address(1L, "New York", "Sesame Street", "United States");
actual.setAddress(address2);
assertTrue(new ReflectionEquals(expected, "address").matches(actual));
assertTrue(new ReflectionEquals(expected.getAddress()).matches(actual.getAddress()));
https://www.baeldung.com/junit-assertions
@ParameterizedTest
@JsonFileSource(resources ="/request.json")
public void addClientUsingJsonInput(JsonObject object) throws Exception {
MediaType MEDIA_TYPE_JSON_UTF8 = new MediaType("application", "json", java.nio.charset.Charset.forName("UTF-8"));
mockMvc.perform(post("/client").contentType(MEDIA_TYPE_JSON_UTF8)
.content(object.toString()))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath( "$.errorMessage").doesNotExist())
.andExpect(MockMvcResultMatchers.jsonPath("$.status").value(true))
.andExpect(MockMvcResultMatchers.jsonPath("$.message").value("Client added successfully."))
.andDo(print());
}
https://www.baeldung.com/java-unit-testing-best-practices
The test names should be insightful, and users should understand the behavior and expectation of the test by just glancing at the name itself.
For example, the name of our unit test was testCalculateArea, which is vague on any meaningful information about the test scenario and expectation.
Therefore, we should name a test with the action and expectation such as testCalculateAreaWithGeneralDoubleValueRadiusThatReturnsAreaInDouble, testCalculateAreaWithLargeDoubleValueRadiusThatReturnsAreaAsInfinity.
However, we can still improve the names for better readability.
It’s often helpful to name the test cases in given_when_then to elaborate on the purpose of a unit test:
public class CircleTest {
//...
@Test
public void givenRadius_whenCalculateArea_thenReturnArea() {
//...
}
@Test
public void givenDoubleMaxValueAsRadius_whenCalculateArea_thenReturnAreaAsInfinity() {
//...
}
}
https://www.baeldung.com/parameterized-tests-junit-5
static Stream<Arguments> arguments = Stream.of(
Arguments.of(null, true), // null strings should be considered blank
Arguments.of("", true),
Arguments.of(" ", true),
Arguments.of("not blank", false)
);
@ParameterizedTest
@VariableSource("arguments")
void isBlank_ShouldReturnTrueForNullOrBlankStringsVariableSource(
String input, boolean expected) {
assertEquals(expected, Strings.isBlank(input));
}
@Test
void givenValidUser_whenSaveUser_thenSucceed(@Mock MailClient mailClient) {
// Given
user = new User("Jerry", 12);
when(userRepository.insert(any(User.class))).then(new Answer<User>() {
int sequence = 1;
@Override
public User answer(InvocationOnMock invocation) throws Throwable {
User user = (User) invocation.getArgument(0);
user.setId(sequence++);
return user;
}
});
userService = new DefaultUserService(userRepository, settingRepository, mailClient);
// When
User insertedUser = userService.register(user);
// Then
verify(userRepository).insert(user);
assertNotNull(user.getId());
verify(mailClient).sendUserRegistrationMail(insertedUser);
}
https://www.baeldung.com/java-mockito-private-fields
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class MockServiceUnitTest {
private Person mockedPerson;
@BeforeEach
public void setUp(){
mockedPerson = mock(Person.class);
}
}
@Test
void givenNameChangedWithReflectionUtils_whenGetName_thenReturnName() throws Exception {
MockService mockService = new MockService();
Field field = ReflectionUtils
.findFields(MockService.class, f -> f.getName().equals("person"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.get(0);
field.setAccessible(true);
field.set(mockService, mockedPerson);
when(mockedPerson.getName()).thenReturn("Jane Doe");
Assertions.assertEquals("Jane Doe", mockService.getName());
}
https://www.baeldung.com/java-mockito-singleton
@Test
void givenValueExistsInCache_whenGetProduct_thenDAOIsNotCalled_mockingStatic() {
ProductDAO productDAO = mock(ProductDAO.class);
CacheManager cacheManager = mock(CacheManager.class);
Product product = new Product("product1", "description");
try (MockedStatic<CacheManager> cacheManagerMock = mockStatic(CacheManager.class)) {
cacheManagerMock.when(CacheManager::getInstance).thenReturn(cacheManager);
when(cacheManager.getValue(any(), any())).thenReturn(product);
ProductService productService = new ProductService(productDAO);
productService.getProduct("product1");
Mockito.verify(productDAO, times(0)).getProduct(any());
}
}
https://www.baeldung.com/junit-test-abstract-class
public abstract class AbstractMethodCalling {
public abstract String abstractFunc();
public String defaultImpl() {
String res = abstractFunc();
return (res == null) ? "Default" : (res + " Default");
}
}
@Test
public void givenDefaultImpl_whenMockAbstractFunc_thenExpectedBehaviour() {
AbstractMethodCalling cls = Mockito.mock(AbstractMethodCalling.class);
Mockito.when(cls.abstractFunc())
.thenReturn("Abstract");
Mockito.doCallRealMethod()
.when(cls)
.defaultImpl();
assertEquals("Abstract Default", cls.defaultImpl());
}
SourceCode (https://github.com/effective-software-testing/code)
6장 테스트 더블과 모의 객체
스텁
스텁은 테스트 과정에서 수행된 호출에 대해 하드 코딩된 응답을 제공한다.
모의 객체
모의객체는 스텁 그 이상의 역할을 한다. 예를 들어 getAllInvoices메서드가 한번만 호출되기 바란다면 한번만 호출됐다라는 단언문을 작성할 수 있다.
스파이
스파이는 의존성을 감시한다. 스파이는 실제 객체를 감싸서 그 행동을 관찰한다. 스파이는 특정 맥락에서 사용된다.
Mockito
Mock(<class>)
when(<mock>.<method>).thenReturn(<value>)
verify(<mock>).<method>
의존성 스텁화
InvoiceFilter에 대한 단위 테스트로 초점을 바꿔보자.
모의 객체와 기댓값
verify(sap, times(2)).send(any(Invoice.class)); // 어떤 송장에 대해 send 메서드가 정확히 두번 호출되었음을 검증한다.
verify(sap, times(1)).send(mauricio); // mauricio 송장에 대해 send 메서드가 정확히 한번 호출되었음을 검증한다.
verify(sap, times(1)).send(frank); // frank 송장에 대해 send 메서드가 정확히 한번 호출되었을 검증한다.
verify(sap, never()).send(any(Invoice.class)); // 어떤 송장에 대해서도 send() 메서드가 호출되지 않았음을 검증한다.
인수 포획
예외를 던지는 모의 객체