How to mock Feign Client - spring-boot

I can't mock my Feign Client using Mockito.
MyService.class
#Service
#AllArgsConstructor
public class MyService implements IMyService{
private static final Logger LOGGER = LoggerFactory.getLogger(MyService .class);
private final MyRepository repository;
private final MyFeignClient myFeignClient;
#Autowired
private MyDao myDao;
#Override
#Async
public void process(Map<UUID, Long> command) {
DocIds docIds = getDocIds(command.values().stream().findFirst().get());
archiveData(command.keySet().stream().findFirst().get(), documentIds.getIds());
}
private DocumentIds getDocIds(Long retentionId) {
return myFeignClient.getDocumentIds(retentionId);
}
private void archiveData(UUID execId, List<Long> docIds) {
List<MyDocument> myDocs= prepareMyDocs(docIds);
repository.saveAll(myDocs);
}
And my test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class ArchiveServiceTest {
#Autowired
ArchiveService archiveService;
#MockBean
MyDao myDao;
#MockBean
DocRepository archiveRepository;
#MockBean
private MyFeignClient myFeignClient;
#Test
public void shouldReturnTheSameNumberOfDocumentsToArchive() {
//given
List<DocData> documentData = prepareDocumentData();
// doReturn(new DocIds()).when(myFeignClient).getDocumentIds(1000L);
DocumentIds documentIds = new DocumentIds();
documentIds.setIds(Arrays.asList(1L, 2L));
when(myFeignClient.getDocIds(any())).thenReturn(documentIds);
when(documentDataDao.getAllDocumentData(anyList())).thenReturn(documentData);
doNothing().when(archiveRepository.saveAll(any()));
//when
Map<UUID, Long> command = new HashMap<>();
command.put(UUID.randomUUID(), 1000L);
archiveService.process(command);
//then
...
MyFeignClient:
#FeignClient(name = "myFeignClient", url = "${feign.searcher.path}")
public interface MyFeignClient{
#RequestMapping(method = RequestMethod.GET, path = "/document/path/{id}")
DocIds getDocumentIds(#PathVariable("id") Long id);
}
When running a test,
return myFeignClient.getDocumentIds(retentionId);
returns NULL. Why?
I don't have more ideas. I don't want to use WireMock. The same happens with my documentDataDao that doesn't return any values (null) specified in thenReturn() clause.

Have You tried it this way:
Mockito.when(myFeignClient.getDocumentIds(Mockito.eq(1000L))).thenReturn(new DocIds());
In You example, mock is commented out ;)
// doReturn(new DocIds()).when(myFeignClient).getDocumentIds(1000L);
But I'm sure it is just a bug in Your example.

Related

Spring WebFluxTest add Pageable resolver

I want to test my Spring controller using #WebFluxTest(MyController.class)
My test :
#WebFluxTest({MyController.class})
class MyControllerTest {
#MockBean
private MyService myService;
#Autowired
private WebTestClient client;
#Test
void myTest() {
List<Integer> list = List.of(1,2,3);
Page<Integer> expected = new PageImpl<>(list);
String buCode = TestUtils.getRandomString();
when(myService.method(
any(), // Pageable
anyString()
)).thenReturn(Mono.just(expected));
client.get()
.uri("/path?arg=test")
.exchange()
.expectStatus()
.isOk();
}
}
My controller
#RestController
#RequestMapping("path")
#AllArgsConstructor
public class MyController {
private MyService myService;
#GetMapping
Mono<Page<Integer>> searchSeller(
Pageable pageable,
#RequestParam(required = false) String arg
) {
return myService.search(pageable, arg);
}
}
But I have this exception when I execute my test :
No primary or default constructor found for interface org.springframework.data.domain.Pageable
I think the solution is maybe to register the org.springframework.data.web.ReactivePageableHandlerMethodArgumentResolver but I don't know how to register it into the WebTestClient
Have you some clues to fix this issue ?

MapStruct mapper not initialized with autowired when debug

I use spring boot 2.3.2 with mapstruct.
In a service class I have a mapper who have an autowired annotation.
#Service
public BillingService{
private BillingRepository billingRepository;
#Autowired
private BillingMapper billingMapper;
#Autowired
public BillingService (BillingRepository billingRepository){
this.billingRepository=billingRepository;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
#RunWith(MockitoJUnitRunner.class)
public class BillingServiceTest{
#Mock
BillingRepository billingRepository;
private BillingService bilingService;
#Spy
private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#BeforeEach
public void setup(){
MockitoAnnotations.initMocks(this);
billingService = new BillingService();
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
When I debug and I'm stuck in getBilling method in BillingService Class, billingMapper is alway null;
You are using very strange configuration. First of all you have method returning BillingService that doesn't specify any return value so this would not even compile. I suggest following way:
#Service
public BillingService{
private final BillingRepository billingRepository;
private final BillingMapper billingMapper;
// constructor with bean injection
public BillingService(final BillingRepository billingRepository,
final BillingMapper billingMapper) {
this.billingRepository = billingRepository;
this.billingMapper = billingMapper;
}
public void getBilling(Long billingId){
....
billingMapper.convertTo(...);
}
}
Then you can configure your test like following:
#RunWith(SpringJUnit4ClassRunner.class)
public class BillingServiceTest {
#Spy private BillingMapper billingMapper = Mappers.getMapper(BillingMapper.class);
#MockBean private BillingRepository billingRepository;
#Autowired private BillingService billingService;
#TestConfiguration
static class BillingServiceTestContextConfiguration {
#Bean public BillingService billingService() {
return new BillingService(billingRepository, billingMapper);
}
}
#Test
public void testGetBilling(){
List<Billing> billings = new ArrayList<>();
...
List<BillingPayload> payloads = new ArrayList<>();
when(billingRepository.findById(1L)).thenReturn(); // someResult
when(billingMapper.convertTo(billings)).thenReturn(payloads);
bilingService.getBilling(1l);
}
}
#Mapper(componentModel="spring")
public interface BillingMapper{
...
}
Similiar configuration should work. Main problem is that you are mixing #Autowired with setter/constructor injection (don't know since your weird method inside BillingService. Also dont know why you use #Spy annotation when you are tryning to Mock interface. #Spy is used to mock actual instance, while #Mock is mocking Class type. I would stick with #MockBean private BillingMapper billingMapper instead if BillingMapper is specified as Bean in your application.

#MockBean-instance used via #Autowire before method injection

In my JUNIT5-test I want to mock a bean by #MockBean. In my #BeforeEach - method the calls are injected.
But other beans #Autowire-ing the #MockBean are instantiated with the #MockBean before the method injection. This is weird and gives me NPEs. How can I force method injection before use of the #MockBean?
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration("classpath:context/authenticationStaff.xml")
#EnableAutoConfiguration
public class PasswordPolicyServiceTest {
private final List<Reference> bcryptDigestRefs = new ArrayList<>();
private final DigestHistoryRule bcryptDigestRule = new DigestHistoryRule(new BCryptHashBean());
#MockBean
private SystemConfiguration systemConfiguration;
#BeforeEach
public void initMock() {
MockitoAnnotations.initMocks(this);
Arrays.asList(SystemConfigKey.values()).forEach(key -> {
Mockito.when(systemConfiguration.getConfig(key)).thenReturn(getConfig(key, key.getDefaultValue()));
});
Mockito.when(systemConfiguration.getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH)).thenReturn(getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH, "5"));
A failing class is:
#Service
public class SessionCacheManager {
private final Ehcache ehCache;
private final Cache<String, SessionVerificationType> sessionCache;
private final SystemConfiguration systemConfiguration;
#Autowired
public SessionCacheManager(final Ehcache ehCache, final SystemConfiguration systemConfiguration) {
this.ehCache=ehCache;
this.systemConfiguration=systemConfiguration;
SystemConfigType systemConfig = systemConfiguration.getConfig(SystemConfigKey.SESSION_MAX_NUMBER);
Integer numberOfParalledSessions = systemConfig.getIntegerValue();
CacheManager cacheManager=ehCache.registerNewCacheManager(CACHE_MANAGER);
sessionCache = cacheManager.createCache(CACHE_NAME,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, SessionVerificationType.class, ResourcePoolsBuilder.heap(numberOfParalledSessions)));
}
As I can see (with debug), the "SessionCacheManager" uses the mocked "SystemConfiguration" but systemConfiguration.getConfig(SystemConfigKey.SESSION_MAX_NUMBER); returns a null.
I helped myself, though I do not like my solution. It is more a trick than a solution. But I cannot think of something else right now.
I change the #ContextConfiguration to:
#ContextConfiguration(locations = "/context/authenticationStaff.xml", classes = { SpringApplicationContext.class })
The XML is setup, that it cannot autodetect the class "SystemConfiguration.class".
Instead of that the "SpringApplicationContext.class" provides the "SystemConfiguration.class" as a mocked bean.
#Configuration
public class SpringApplicationContext {
#Mock
private SystemConfiguration mockedSystemConfiguration;
#Bean
public SystemConfiguration systemConfiguration() {
MockitoAnnotations.initMocks(this);
Arrays.asList(SystemConfigKey.values()).forEach(key -> {
Mockito.when(mockedSystemConfiguration.getConfig(key)).thenReturn(getConfig(key, key.getDefaultValue()));
});
Mockito.when(mockedSystemConfiguration.getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH)).thenReturn(getConfig(SystemConfigKey.MIN_PASSWORD_LENGTH, "5"));
Mockito.when(mockedSystemConfiguration.getConfig(SystemConfigKey.PASSWORD_BCRYPTENCODER_COSTFACTOR)).thenReturn(getConfig(SystemConfigKey.PASSWORD_BCRYPTENCODER_COSTFACTOR, "5"));
return mockedSystemConfiguration;
}
private SystemConfigType getConfig(SystemConfigKey key, String value) {
SystemConfigType config = new SystemConfigType();
config.setKey(key);
config.setValue(value);
return config;
}
The test-code now looks like this:
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(locations = "/context/authenticationStaff.xml", classes = { SpringApplicationContext.class })
#EnableAutoConfiguration
public class PasswordPolicyServiceTest {
#Autowired
private PasswordPolicyService passwordPolicyService;
#Autowired
private PasswordHandlerService passwordHandlerService;
#Autowired
private SystemConfiguration systemConfiguration;
private final List<Reference> bcryptDigestRefs = new ArrayList<>();
private final DigestHistoryRule bcryptDigestRule = new DigestHistoryRule(new BCryptHashBean());
#BeforeEach
public void initMock() {
MockitoAnnotations.initMocks(this);
String password=passwordHandlerService.getPassword("my$Password");
bcryptDigestRefs.add(
new HistoricalReference(
"bcrypt-history",
password));
}
This works, but is not a nice solution. Other recommendations are very welcome.

Testing REST API with MOCKITO

I have a problem with testing the REST API using MOCKITO
I have an example of the rest controller code:
#RestController
#RequestMapping(path = "api/workers")
public class WorkOfferController {
#Autowired
private WorkerService workerService;
#PostMapping(value = "/lists", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity searchWorker(#RequestBody RecruitmentSearchRequest recruitmentSearchRequest, #RequestParam("page") int page, #RequestParam("size") int size,) throws NoSuchFieldException {
System.err.print('WorkerController');
return workerService.getWorkers(recruitmentSearchRequest, page, size);
}
}
And the right service for it:
#Service
#Transactional
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class WorkerService {
private final WorkerRepistory workerRepository;
private final UserRepository userRepository;
public ResponseEntity getWorkers(RecruitmentSearchRequest recruitmentSearchRequest,int pageNumber, int size) throws NoSuchFieldException {
System.err.print('WorkerService');
...
}
}
I want to test whether everything is okay under the url with the right data. I do not want to use this database because I prefer Mockito.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Appp2workApplication.class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class PharmacyWorkOfferRestDocsTests {
private MockMvc mockMvc;
#InjectMocks
private WorkOfferController workOfferController;
#Mock
private WorkerService workerService;
#Mock
UserRepository userRepository;
#Mock
WorkerRepistory workerRepository;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.standaloneSetup(workOfferController).build();
}
#Test
public void searchWorkOfferListWithParameters() throws Exception {
String payload = "{\n" +
"\t\"name\":\"tomek\",\n" +
"\t\"searchFromSalary\":5,\n" +
"}";
Mockito.doNothing().when(userRepository.save(Mockito.anyObject()));
Mockito.when(searchService.getRecruitmentOfferJobListWithParameters(Mockito.anyObject())).thenReturn(list);
this.mockMvc.perform(post("/api/workers/lists?page=0&size=20").contentType(MediaType.APPLICATION_JSON).content(payload))
.andExpect(status().isOk());
}
}
And I have a problem that with this approach the test comes to me in the controller and displays "WorkerController" but I do not want to enter the service from this controller, and it returns 200, but it really only came to the controller and that's it. This is probably because WorkerService is as Mock but I tried to give it as eg Autowired or InjectMock and it is still the same.
What do I do wrong that I enter into the controller but I do not want to use this controller for the appropriate service?

Mockito not mocking my #Autowired class

I came across a problem, while mocking my class i get NullPointerException on mocked object and I cant figure out why mockito is not mocking only one class annotated by #Mock. Do someone have any idea what's the problem there ?
Tested class :
public class TimeoutJpaTransactionManager extends JpaTransactionManager {
private final String TRANSACTION_METHOD_NAME= String.join(".", ZapisPakietuReceptService.class.getName(), "transactionMethod");
#Autowired
private TransactionTimeoutConfig transactionTimeoutConfig;
public TimeoutJpaTransactionManager(EntityManagerFactory emf) {
super(emf);
}
protected int determineTimeout(TransactionDefinition definition) {
if (TRANSACTION_METHOD_NAME.equals(definition.getName())) {
return transactionTimeoutConfig.getSgr();
}
return super.getDefaultTimeout();
}
}
Autowired class :
#Getter
#Setter
#Component
#ConfigurationProperties(prefix = "transaction.timeout")
public class TransactionTimeoutConfig {
private int sgr;
}
Test class:
public class TimeoutJpaTransactionManagerTest {
#InjectMocks
private TimeoutJpaTransactionManager timeoutJpaTransactionManager;
#Mock
private EntityManagerFactory entityManagerFactory;
#Mock
private TransactionDefinition transactionDefinition;
#Mock
private TransactionTimeoutConfig transactionTimeoutConfig;
private final String TRANSACTION_METHOD_NAME_MOCK= String.join(".", ZapisPakietuReceptService.class.getName(), "transactionMethod");
private final int TIMEOUT_VALUE= 30;
#BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testDetermineTimeout() {
//given
Mockito.when(transactionDefinition.getName()).thenReturn(TRANSACTION_METHOD_NAME_MOCK);
Mockito.when(transactionTimeoutConfig.getSgr()).thenReturn(TIMEOUT_VALUE);
//when
int result = timeoutJpaTransactionManager.determineTimeout(transactionDefinition);
//then
Assertions.assertThat(result).isEqualTo(TIMEOUT_VALUE);
}
}
Whats weird and i cant undestand is that by adding another line like that all seems to work and TransactionDefinition is really mocked :
#BeforeMethod
public void setUp() {
MockitoAnnotations.initMocks(this);
MockitoAnnotations.initMocks(this);
}
Thanks to the M. Deinum comment i was able to figure it out, it seems the problem was with mixing field and constructor injections. Solution is to either move dependencies to the constructor and then wire it there or modify the current test case to
#InjectMocks
private TimeoutJpaTransactionManager timeoutJpaTransactionManager;
#Mock
private TransactionDefinition transactionDefinition;
#Mock
private TransactionTimeoutConfig transactionTimeoutConfig;
private EntityManagerFactory entityManagerFactory;
private final String TRANSACTION_METHOD_NAME_MOCK= String.join(".", ZapisPakietuReceptService.class.getName(), "transactionMethod");
private final int TIMEOUT_VALUE= 30;
#BeforeMethod
public void setUp() {
entityManagerFactory = Mockito.mock(EntityManagerFactory.class);
timeoutJpaTransactionManager = new TimeoutJpaTransactionManager(entityManagerFactory);
MockitoAnnotations.initMocks(this);
}

Resources