티스토리 뷰
1. 테스트를 하다 보면 언제 any, anyLong 같은 값을 사용할지 아니면 진짜 값을 넣어주어야 할지 혼란스럽다.
2. 아래 같은 유닛테스트의 경우는 모두 any, anyLong을 사용하고 있다.
2-1 예제는 Service 구현한 클래스를 테스트하는 것으로
2-2 Mockito fixture로는 repository나 conveter를 사용하고 있다.
2-3 fixture가 반환해야 할 값들을 세팅할 때 when을 사용하는데 여기의 인자들은 모두 any 계열을 사용한다.
package pe.pilseong.recipe.service;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import pe.pilseong.recipe.command.RecipeCommand;
import pe.pilseong.recipe.coverter.RecipeCommandToRecipe;
import pe.pilseong.recipe.coverter.RecipeToRecipeCommand;
import pe.pilseong.recipe.domain.Difficulty;
import pe.pilseong.recipe.domain.Recipe;
import pe.pilseong.recipe.repository.RecipeRepository;
import static org.junit.Assert.assertThrows;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.junit.Before;
public class RecipeServiceImplTest {
RecipeServiceImpl recipeService;
@Mock
RecipeRepository recipeRepository;
@Mock
RecipeCommandToRecipe recipeConverter;
@Mock
RecipeToRecipeCommand commandConverter;
public static final Long RECIPE_ID = 1L;
public static final Integer COOK_TIME = Integer.valueOf("5");
public static final Integer PREP_TIME = Integer.valueOf("7");
public static final String DESCRIPTION = "my recipe";
public static final String DIRECTIONS = "Directions";
public static final Difficulty DIFFICULTY = Difficulty.EASY;
public static final String SOURCE = "source";
public static final String URL = "url";
public static final Long CAT_ID1 = 1L;
public static final Long CAT_ID2 = 2L;
public static final Long CAT_ID3 = 3L;
public static final Long INGRED_ID1 = 1L;
public static final Long INGRED_ID2 = 2L;
public static final Long INGRED_ID3 = 3L;
public static final Long NOTE_ID = 9L;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
recipeService = new RecipeServiceImpl(recipeRepository, recipeConverter, commandConverter);
}
@Test
public void getRecipes() {
Recipe recipe = new Recipe();
HashSet<Recipe> recipeData = new HashSet<>();
recipeData.add(recipe);
when(recipeRepository.findAll()).thenReturn(recipeData);
Set<Recipe> recipes = recipeService.getRecipes();
assertEquals(recipes.size(), 1);
// check to see how many times findAll method invoked
verify(recipeRepository, times(1)).findAll();
}
@Test
public void findById() {
Recipe returnRecipe = Recipe.builder().id(1L).build();
when(recipeRepository.findById(anyLong())).thenReturn(Optional.of(returnRecipe));
Recipe recipe = recipeService.findById(1L);
assertEquals(1L, recipe.getId());
verify(recipeRepository).findById(anyLong());
verify(recipeRepository, never()).findAll();
}
@Test
public void findCommandById() {
Recipe returnRecipe = Recipe.builder().id(1L).build();
RecipeCommand command = new RecipeCommand();
command.setId(1L);
when(recipeRepository.findById(anyLong())).thenReturn(Optional.of(returnRecipe));
when(commandConverter.convert(any())).thenReturn(command);
RecipeCommand retrieveCommand = recipeService.findCommandById(RECIPE_ID);
assertEquals(1L, retrieveCommand.getId());
verify(recipeRepository).findById(anyLong());
verify(commandConverter).convert(any());
verify(recipeRepository, never()).findAll();
}
@Test
public void findByIdWithError() {
// Recipe returnRecipe = Recipe.builder().id(1L).build();
when(recipeRepository.findById(anyLong())).thenReturn(null);
assertThrows(RuntimeException.class, () -> recipeService.findById(anyLong()));
}
@Test
public void save() {
Recipe returnRecipe = Recipe.builder().id(1L).build();
when(recipeRepository.save(any())).thenReturn(returnRecipe);
Recipe saveRecipe = recipeService.save(any());
assertNotNull(saveRecipe);
assertEquals(1L, saveRecipe.getId());
}
@Test
public void saveCommand() {
Recipe returnRecipe = Recipe.builder().id(1L).build();
RecipeCommand command = new RecipeCommand();
command.setId(1L);
when(recipeRepository.save(any())).thenReturn(returnRecipe);
when(commandConverter.convert(any())).thenReturn(command);
RecipeCommand recipeCommand = recipeService.saveRecipeCommand(any());
assertNotNull(recipeCommand);
assertEquals(1L, recipeCommand.getId());
}
@Test
public void testDeleteById() {
//given
Long targetId = 1L;
//when
recipeService.deleteById(targetId);
// no ''when', since method has void return type
//then
verify(recipeRepository, times(1)).deleteById(anyLong());
}
}
3. 아래의 코드도 Service 구현체에 대한 테스트인데, 이 경우는 1L, 3L 같은 값을 사용하고 있다.
3-1 여기에서는 특정 값을 사용하는 이유는 테스트에 필요하기 때문이다.
3-2 즉 fixture에서 값을 받아오는 것은 어떠한 경우에도 동일한 객체가 반환되기 때문에 특정한 값이 필요없지만,
3-3 아래처럼 service의 특정함수 내에서 fixture에서 받환받은 객체를 가지고 작업을 할 때는
3-3-1 그것을 위한 조건은 모두 충족되어야 하기 때문이다.
package pe.pilseong.recipe.service;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import pe.pilseong.recipe.command.IngredientCommand;
import pe.pilseong.recipe.coverter.IngredientToIngredientCommand;
import pe.pilseong.recipe.coverter.UnitOfMesureToUnitOfMeasureCommand;
import pe.pilseong.recipe.domain.Ingredient;
import pe.pilseong.recipe.domain.Recipe;
import pe.pilseong.recipe.repository.RecipeRepository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Optional;
import org.junit.Before;
public class IngredientServiceImplTest {
IngredientServiceImpl ingredientService;
@Mock
RecipeRepository recipeRepository;
IngredientToIngredientCommand ingredientCommandConverter;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
ingredientCommandConverter = new IngredientToIngredientCommand(new UnitOfMesureToUnitOfMeasureCommand());
ingredientService = new IngredientServiceImpl(recipeRepository, ingredientCommandConverter);
}
@Test
public void testFindByRecipeIdAndIngredientId() {
// given
Recipe recipe = Recipe.builder().id(1L).build();
Ingredient ingredient1 = new Ingredient();
ingredient1.setId(1L);
Ingredient ingredient2 = new Ingredient();
ingredient1.setId(2L);
Ingredient ingredient3 = new Ingredient();
ingredient1.setId(3L);
recipe.addIngredient(ingredient1);
recipe.addIngredient(ingredient2);
recipe.addIngredient(ingredient3);
Optional<Recipe> optionalRecipe = Optional.of(recipe);
// when
when(recipeRepository.findById(anyLong())).thenReturn(optionalRecipe);
IngredientCommand ingredientCommand =
ingredientService.findByRecipeIdAndIngredientId(anyLong(), 3L);
// then
assertNotNull(ingredientCommand);
assertEquals(3L, ingredientCommand.getId());
assertEquals(1L, ingredientCommand.getRecipeId());
verify(recipeRepository, times(1)).findById(anyLong());
}
}
3-4 아래의 코드가 테스트 대상 코드인데 받아온 recipe 내의 category를 찾는 로직이 있는데,
3-4-1 ingredientId가 정확하게 명시되어 있지 않으면 수행자체가 될 수 없다.
3-4-2 반면에 recipeId의 경우는 fixture에서 사용되기 때문에 anyLong도 가능하다.
3-4-2-1 즉 위의 서비스 테스트 메소드 호출 부분을 아래처럼 수정할 수도 있다.
// 원래 코드
ingredientService.findByRecipeIdAndIngredientId(1L, 3L);
// 가능한 코드
ingredientService.findByRecipeIdAndIngredientId(anyLong(), 3L);
// 불가능한 코드
ingredientService.findByRecipeIdAndIngredientId(anyLong(), anyLong());
package pe.pilseong.recipe.service;
import java.util.Optional;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import pe.pilseong.recipe.command.IngredientCommand;
import pe.pilseong.recipe.coverter.IngredientToIngredientCommand;
import pe.pilseong.recipe.domain.Ingredient;
import pe.pilseong.recipe.repository.RecipeRepository;
@Slf4j
@Service
@RequiredArgsConstructor
public class IngredientServiceImpl implements IngredientService {
private final RecipeRepository recipeRepository;
private final IngredientToIngredientCommand ingredientCommandConverter;
@Override
@Transactional
public IngredientCommand findByRecipeIdAndIngredientId(final long recipeId, final long ingredientId) {
log.debug("\nfindByRecipeIdAndIngredientId in IngredientServiceImpl");
Optional<Ingredient> optioanlIngredient =
recipeRepository.findById(recipeId).map(recipe-> {
Optional<Ingredient> ingredientOptioanl = recipe.getIngredients().stream()
.filter(ingredient-> ingredient.getId().equals(ingredientId))
.findFirst();
if (ingredientOptioanl.isPresent()) {
return ingredientOptioanl.get();
} else {
return null;
}
});
if (!optioanlIngredient.isPresent()) {
log.error("ingredient id not found " + ingredientId);
}
Ingredient ingredient = (Ingredient)optioanlIngredient.get();
log.debug("ingredient fetched :: " + ingredient.toString());
return ingredientCommandConverter.convert(ingredient);
}
}
'Spring > Spring Test' 카테고리의 다른 글
Spring Test : Embedded MongoDB 통합테스트 시 connection closed (0) | 2020.08.30 |
---|---|
Spring Test : Repository Integration Test with @RunWith, @Mongo (0) | 2020.08.21 |
Spring Test : Integration Test (0) | 2020.08.04 |
Spring Test : JUnit 5 Mockito Unit Test (0) | 2020.08.03 |
Spring Test : Spring Boot 2.1 이전 버전에서의 Junit 5 설정하기 (0) | 2020.08.02 |
- Total
- Today
- Yesterday
- 도커 개발환경 참고
- AWS ARN 구조
- Immuability에 관한 설명
- 자바스크립트 멀티 비동기 함수 호출 참고
- WSDL 참고
- SOAP 컨슈머 참고
- MySql dump 사용법
- AWS Lambda with Addon
- NFC 드라이버 linux 설치
- electron IPC
- mifare classic 강의
- go module 관련 상세한 정보
- C 메모리 찍어보기
- C++ Addon 마이그레이션
- JAX WS Header 관련 stackoverflow
- SOAP Custom Header 설정 참고
- SOAP Custom Header
- SOAP BindingProvider
- dispatcher 사용하여 설정
- vagrant kvm으로 사용하기
- git fork, pull request to the …
- vagrant libvirt bridge network
- python, js의 async, await의 차이
- go JSON struct 생성
- Netflix Kinesis 활용 분석
- docker credential problem
- private subnet에서 outbound IP 확…
- 안드로이드 coroutine
- kotlin with, apply, also 등
- 안드로이드 초기로딩이 안되는 경우
- navigation 데이터 보내기
- 레이스 컨디션 navController
- raylib
- 설정하기
- login
- 매핑
- hibernate
- Many-To-Many
- Validation
- crud
- Spring Security
- Spring
- Security
- form
- RestTemplate
- spring boot
- 로그인
- XML
- 설정
- 자바
- 상속
- 하이버네이트
- one-to-many
- mapping
- WebMvc
- jsp
- 스프링
- Rest
- Angular
- 스프링부트
- one-to-one
- 외부파일
- MYSQL