#spring-boot #mockito #powermockito #spring-boot-test
#spring-boot #mockito #powermockito #spring-boot-test
Вопрос:
Предположим, я реализовал следующий класс обслуживания:
@Service
@RequiredArgsConstructor
@Transactional
public class ItemService {
private final ItemRepository itemRepository;
private final ItemCreatorFactory itemCreatorFactory;
private final CommonClient commonClient;
private final HelperService helperService;
private final PrivilegeUtil privilegeUtil;
public void addItem() {
//How to only mock this in tests and other defaults to normal Autowired injection
if(!privilegeUtil.isAdmin("bla bla")) {
Throw new InvalidOperationException("Only super admin can insert data.");
}
helperService.doSomeRealLogic();
commonClient.callAnotherExternalAPI();
itemRepository.save(myObject);
}
// other methods
}
И мне нужно протестировать вставку нового элемента в базу данных, но перед этим я выполняю дополнительную логику, и если все в порядке, я могу безопасно вставить в базу данных.
Теперь мне нужно использовать mockito, поэтому я издеваюсь только над классом privilegeUtil, но мне нужно, чтобы все остальные экземпляры внутри класса ItemService были автоматически подключены, не издеваясь над ним.
Итак, я перехожу к следующему тестовому классу:
@RunWith(SpringRunner.class)
public class ItemTests {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@InjectMocks//Only mocks privilegeUtil but other instances is all null :(
private ItemService itemService;//So how i get autowired item service instance but only mock privilegeUtil instance inside it and all other instances go for normal instantiation ???
@Mock
private PrivilegeUtil privilegeUtil;
@Test
public void testAddNewItem() {
String email = "super@mail.com";
Mockito.when(privilegeUtil.isSuperAdmin(email)).thenReturn(true);
//Here i need to test adding item normally
//But only skip checking isSuperAdmin part inside addItem method
itemService.addItem(itemRequest, email);
}
}
Итак, как я получаю экземпляр службы autowired item, но внутри него есть только экземпляр mock privilegeUtil, а все остальные экземпляры идут для обычного создания экземпляра???
Возможно ли это? а если нет, то что вы можете предложить?
Ответ №1:
Да, это возможно. Поскольку вы упоминаете, что тестируете некоторую логику, связанную с базой данных, я думаю, имеет смысл использовать @DataJpaTest
, чтобы позволить Spring создать для вас нарезанный контекст со всеми компонентами, связанными с JPA / database.
Затем вы уже можете автоматически подключить свой репозиторий к своему тесту.
Что осталось, так это вручную создать все остальные обычные компоненты, используя, например @TestConfiguration
. Те классы, которые вы хотите имитировать, аннотируйте их, @MockBean
чтобы Spring помещал отредактированную версию в нарезанный контекст.
@RunWith(SpringRunner.class)
@DataJpaTest
public class ItemTests {
@TestConfiguration
static class TestConfig {
@Bean
public ItemCreatorFactory itemCreatorFactory() {
return new ItemCreatorFactory();
}
// .. manually create all other beans
}
@Autowired
private ItemRepository itemRepository;
@Autowired
private ItemCreatorFactory itemCreatorFactory;
@Autowired
private CommonClient commonClient;
@Autowired
private HelperService helperService;
@MockBean
private PrivilegeUtil privilegeUtil;
@Before
public void setUp() throws Exception {
this.itemService = new ItemService(itemRepository, itemCreatorFactory, commonClient, helperService, mockedPrivilegeUtil);
}
@Test
public void testAddNewItem() {
String email = "super@mail.com";
Mockito.when(privilegeUtil.isSuperAdmin(email)).thenReturn(true);
itemService.addItem(itemRequest, email);
}
}
… и тогда вы, наконец, можете вручную создать свой ItemService
экземпляр и передать все свои экземпляры конструктору.