I want to edit this code to use mock for services. This code is working fine:
#Service
public class BinCountryCheckFilterImpl {
private RiskFilterService riskFilterService;
private Terminals terminal;
#Autowired
public BinCountryCheckFilterImpl(Terminals terminal, RiskFilterService riskFilterService) {
this.terminal = terminal;
this.riskFilterService = riskFilterService;
}
public void validateBinCountryCheckFilter(Terminals terminal) throws JAXBException, JsonProcessingException {
List<RiskFilters> filter_list = riskFilterService.findRiskFiltersByTerminalIdAndType(terminal.getId(), "BinCountryCheckFilter");
}
}
JUnit test:
public class BinCountryCheckFilterImplTest {
#Test
public void testBinCountryCheckFilterImpl() throws JsonProcessingException, JAXBException {
//Arrange
RiskFilterService riskFilterService = Mockito.mock(RiskFilterService.class);
Terminals terminal = Mockito.mock(Terminals.class);
BDDMockito.given(
riskFilterService.findRiskFiltersByTerminalIdAndType(anyInt(), anyString()))
.willReturn(riskFiltersList);
int expectedInt = 11;
String expectedString = "BinCountryCheckFilter";
when(terminal.getId()).thenReturn(expectedInt);
BinCountryCheckFilterImpl binCountryCheckFilterImpl =
new BinCountryCheckFilterImpl(terminal, riskFilterService);
//Act
binCountryCheckFilterImpl.validateBinCountryCheckFilter();
//Assert
then(riskFilterService)
.should()
.findRiskFiltersByTerminalIdAndType(expectedInt, expectedString);
}
}
This code is NOT working:
#Service
public class BinCountryCheckFilterImpl {
#Autowired
#Qualifier("riskFilterService")
private RiskFilterService riskFilterService;
public Response validateBinCountryCheckFilter(Merchants merchant, Contracts contract, Terminals terminal,
PaymentTransaction message, HttpServletRequest request) throws JAXBException, JsonProcessingException {
...........
}
}
For some reason riskFilterService in null when I run the JUnit test. Is there some way to implement the service with the JUnit test properly?
In my case I don't want to call terminal from the constructor but from the Java method validateBinCountryCheckFilter(...) and riskFilterService as Autowired service.
The singleton bean BinCountryCheckFilterImpl cannot have the per-request value terminal as a field, i.e. as a parameter on the constructor when the code is deployed but it's working with the JUnit test. If the terminal value must be passed as a method parameter the JUnit code won't run.
Can you advice, please?
I'm assuming you're using JUnit4. The easiest way to do this is to use the MockitoJUnitRunner like this:
#RunWith(MockitoJUnitRunner.class)
public class BinCountryCheckFilterImplTest {
#InjectMocks
private BinCountryCheckFilterImpl binCountryCheckFilter;
#Mock
private RiskFilterService riskFilterService;
#Test
public void testBinCountryCheckFilterImpl() {
// test content
}
}
To use JUnit 5 you would change the #RunWith(MockitoJUnitRunner.class) to #ExtendWith(MockitoExtension.class). Of course anything else like #Test used would also need to be from JUnit 5. But, the Mockito #InjectMocks and #Mock annotations are the same as JUnit 4.
However, you can also use the Spring org.springframework.test.util.ReflectionTestUtils to set the field after you call the constructor with the mocked service. I prefer the first way over reflection, but this is closer to your current code.
riskFilterService = Mockito.mock(RiskFilterService.class);
binCountryCheckFilter = new BinCountryCheckFilterImpl();
ReflectionTestUtils.setField(binCountryCheckFilter, "riskFilterService", riskFilterService);
Change existing code to:
public class BinCountryCheckFilterImpl {
private final RiskFilterService riskFilterService;
public BinCountryCheckFilterImpl(#Qualifier("riskFilterService") RiskFilterService riskFilterService) {
this.riskFilterService = riskFilterService;
}
public void validateBinCountryCheckFilter(......) throws JAXBException, JsonProcessingException {
.........................
}
}
Related
I use Spring Boot 5 and JUnit in my project. I create a unit test to test the service.
Here is the service that I am testing:
#Service
#RequiredArgsConstructor
#Slf4j
public class BuilderServiceImpl implements BuilderService{
#Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
#Override
public BuilderDto getByEmail(String email){
}
#Override
public List<BuilderMinDto> getAll() {}
#Override
public List<BuilderMinDto> getAll(int page, int size) {}
#Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
And here is the test class that tests the service above:
#SpringBootTest
#RequiredArgsConstructor
#Slf4j
class BuilderServiceImplTest {
#Mock
private BuilderRepository builderRepository;
#Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
#AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
#Test
void getByEmail(){}
#Test
#Disabled
void getAll() { }
#Test
#Disabled
void testGetAll() {}
#Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test#builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
When I start to run the test class the create method in BuilderServiceImpl fired and on this row:
var str = autoMapper.getDummyText();
I get NullPointerException(autoMapper instance is null).
Here is the definition of AutoMapper class:
#Component
#Slf4j
#RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
As you can see I use #Component annotation to register the AutoMapper class to the IoC container and Autowired annotation to inject it into autoMapper property in BuilderServiceImpl class.
Why autoMapper instance is null? How can I make autoMapper to be initialized?
In order to make #Autowire work you have to use the instance of BuilderServiceImpl (object under test) created by spring itself.
When you create the object like this (by yourself, manually):
#BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring doesn't know anything about this object, hence Autowiring won't work
Another thing that might be useful:
You've used #Mock for BuilderRepository and AdminUserRepository.
This are plain mockito annotation, and if you're using an integration/system test that runs the spring under the hood, probably this is not what you want:
Surely, it will create a mock, but it won't put it onto an application context, and won't substitute the beans of these classes that might have been created by spring.
So if this is what you want to achieve, you should use #MockBean instead.
This annotation belongs to Spring Testing framework rather than a plain mockito annotation.
All-in-all you might end up with something like this:
#SpringBootTest
class MyTest
{
#MockBean
BuilderRepository builderRepo;
#MockBean
AdminUserRepository adminUserRepo;
#Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
#Test
void mytest() {
...
}
}
Add #Autowire annotation Annotations on below fields. Error due your not initialized below object In BuilderServiceImpl
#Autowire
private final BuilderRepository builderRepository;
#Autowire
private final AdminUserRepository adminUserRepository;
Why are you creating BuildService manually? If you do this, set AutoMapper manualy too.
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
You are not using di.
I have a controller class:
public class Controller {
private final IProcessor processor;
public Controller (final ProcessorFactory factory) {
this.processor = factory.getInstance();
}
}
A Factory class to provide the different instances of IProcessor:
#Component
public class ProcessorFactory {
private final Dep1 dep1;
private final Dep2 dep2;
public ProcessorFactory (final Dep1 dep1,
final Dep2 dep2) {
this.dep1= dep1;
this.dep2= dep2;
}
public IProcessor getInstance() {
if (...) {
return new ProcessorA(dep1, dep2);
}
return new ProcessorB(dep1, dep2);
}
}
In my mockito test class where I use Junit5, I am not able to instantiate the IProcessor member and is null:
#WebMvcTest(Controller.class)
public class ControllerTest {
#MockBean
private ProcessorFactory processorFactory ;
#MockBean
private IProcessor processor;
#Autowired
private MockMvc mockMvc;
#Test
public void test1() throws Exception {
when(processor.process(any(Request.class), any(String.class)))
.thenReturn(new BlaBla("Test", "Test"));
String request = ...
this.mockMvc.perform(post("/test/test").contentType(MediaType.APPLICATION_JSON).content(request))
.andDo(print())
.andExpect(status().is2xxSuccessful());
}
}
I am not sure I am using MockBean correctly. Basically I want to mock both the Factory and the Processor.
Since you need to call a mocked method (getInstance()) during Spring context initialization (inside the Controller's constructor), you need to mock the said method in a different way. The mocked bean has to be not only provided as an existing object, but also it should have it's mocked behavior defined.
Addtionally, IProcessor implementations are not configured as Spring beans, so Spring will not inject them - ProcessorFactory calls new explicitly and creates the objects without Spring involvement.
I've created a simple project to reproduce your problem and provide a solution - you can find it here on GitHub if you want to check if the whole thing is working, but here's the most important test snippet (I've simplified the methods a bit):
#WebMvcTest(Controller.class)
class ControllerTest {
private static final IProcessor PROCESSOR = mock(IProcessor.class);
#TestConfiguration
static class InnerConfiguration {
#Bean
ProcessorFactory processorFactory() {
ProcessorFactory processorFactory = mock(ProcessorFactory.class);
when(processorFactory.getInstance())
.thenReturn(PROCESSOR);
return processorFactory;
}
}
#Autowired
private MockMvc mockMvc;
#Test
void test1() throws Exception {
String result = "this is a test";
when(PROCESSOR.process(any()))
.thenReturn(result);
mockMvc.perform(post("/test/test")
.contentType(MediaType.APPLICATION_JSON)
.content("{}"))
.andDo(print())
.andExpect(status().is2xxSuccessful())
.andExpect(content().string(result));
}
}
Basically, the question is in the title.
I faced a problem that in post-construct phase my bean (that is autowired in the bean that is going through post-construct phase right now) is already mocked, but all the behavior described by Mockito.when() doesn't work, all the calls return null.
While searching I found this solution.
But is it possible to make it work without using any 3rd party libraries?
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
#ContextConfiguration(classes = TestApplicationConfiguration.class)
public class ServiceTest {
#Autowired
#Qualifier("test")
private PCLPortType pclPortType;
#MockBean
private ClearingHelper сlearingHelper;
#MockBean
private OrganizationCacheRepository organizationCacheRepository;
#Before
public void setup() throws Exception{
OperationResultWithOrganizationSystemIdMappingList res = new OperationResultWithOrganizationSystemIdMappingList();
when(clearingHelper.getOrgIdSystemIdMapping(any(Keycloak.class))).thenReturn(res);
}
#Test
public void test() throws Exception{
pclPortType.call("123");
}
}
Test config:
#TestConfiguration
public class TestApplicationConfiguration {
#Bean(name = "test")
public PCLPortType pclPortTypeForTest() throws JAXBException {
...
}
#Bean
public Keycloak keycloak() {
return Mockito.mock(Keycloak.class);
}
}
Component where I want to get mocked beans:
#Component
public class OrganizationCacheJob {
private static final Logger logger =
LogManager.getLogger(OrganizationCacheJob.class);
private final ObjectFactory<Keycloak> factory;
private final ClearingHelper clearingHelper;
private final OrganizationCacheRepository organizationCacheRepository;
#Autowired
public OrganizationCacheJob(ObjectFactory<Keycloak> factory,
ClearingHelper clearingHelper,
OrganizationCacheRepository organizationCacheRepository) {
this.factory = factory;
this.clearingHelper = ClearingHelper;
this.organizationCacheRepository = organizationCacheRepository;
}
#PostConstruct
public void updateCacheRepository() {
doUpdateCacheRepository();
}
#Scheduled(cron = "${organization.cache.schedule}")
public void start() {
logger.info("Starting update organization cache.");
doUpdateCacheRepository();
logger.info("Job finished.");
}
private void doUpdateCacheRepository() {
try {
Keycloak keycloak = factory.getObject();
OperationResultWithOrganizationSystemIdMappingList orgIdSystemIdMapping = clearingHelper.getOrgIdSystemIdMapping(keycloak);
if (orgIdSystemIdMapping != null) {
orgIdSystemIdMapping.getContent().forEach(o -> organizationCacheRepository.saveOrgIdsSystemsIdsMappings(o.getOrgId(), o.getId()));
logger.debug("Was saved {} orgIds", orgIdSystemIdMapping.getContent().size());
}
} catch (Exception e) {
logger.error("Error fetching whole mapping for org and systems ids. Exception: {}", e);
}
}
}
So, in post-construct phase of OrganizationCacheJob I want to get res when calling clearingHelper, but instead I get null.
ClearingHelper is a regular Spring bean marked as a #Component with public methods.
Ahh ok I just realized - when you start your test case, whole env is up and running first, then you advance to testing phase. So, translating to your case - first you got injection and post-constructs called, then #Before method is done, thus the result.
So as you can see, code says more than all the words you could put in your original post.
If it is possible for you, use spies insteed of mocks. If it is not possible to construct that, you will have to redesign your tests to not rely on post construct.
In this case, since you want the same post-construct behavior for every test case, provide own factory method for given mock (like you did with keycloak) and move when-doReturn there. It will be guaranteed that it will happen before post construct.
I have a class MyClass which has #Autowired private Environment env; in it. It also has public MyFunctionA(), which calls private MyFunctionB(), and in MyFunctionB(), it calls env.getProperty(propName) which will return a string value from a properties file to MyFunctionA() as a factor in its computation. But currently, when debugging JUnit test, I can't mock env without getting null.
The unit test class MyServiceTest for MyClass starts out as follows:
public class MyServiceTest {
#Mock
final Environment env = Mockito.mock(Environment.class);
MyServiceImpl myService;
#Before
public void setUp() throws Exception {
Mockito.when(this.env.getProperty("myProperty")).thenReturn("1234,2345");
myService = new MyServiceImpl();
Later on, in the unit test function, it calls MyFunctionA() but debugging shows that MyFunctionB() encounters NPE when making the env.getProperty call. What could be wrong? The test looks like this:
#Test
public void myUnitTest() throws IOException {
boolean boolFlag = myService.MyFunctionA();
assertTrue(boolFLag);
}
There are two issues here:
You override the mock object created by the #Mock annotation by explicitly assigning a new (mocked) value to env.
You're missing the #InjectMocks annotation to inject the mocked env to your myService member:
public class MyServiceTest {
#Mock
Environment env; // issue #1
#InjectMocks // issue #2
MyServiceImpl myService = new MyServiceImpl()
#Before
public void setUp() throws Exception {
Mockito.when(this.env.getProperty("myProperty")).thenReturn("1234,2345");
}
// Tests ...
Like #Mureinik said, there were issuing with your object creation. However, if you rely on annotations from Mockito, you need to initialize objects using runner MockitoJUnitRunner or calling MockitoAnnotations.initMocks(this);
Final configuration:
public class MyServiceTest {
#Mock
Environment env;
#InjectMocks
MyServiceImpl myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Mockito.when(this.env.getProperty("myProperty")).thenReturn("1234,2345");
}
// Tests ...
During the test there is a NullPointerException thrown. I tried to debug it and the only thing I worked out was that eventOptional is always null. Just as if Mockito.when().thenReturn() didn't work. Can anybody help? Here's my code for a tested service and for the test itself:
#Service
public class EventService {
#Autowired
public EventService(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
//...
public void updateEvent(EventDTO eventDTO) {
Optional<Event> eventOptional = eventRepository.findOneById(eventDTO.getId());
eventOptional.orElseThrow(() -> new BadRequestException(EVENT_NOT_FOUND));
//...
}
}
And the test class:
#RunWith(MockitoJUnitRunner.class)
public class EventServiceTest {
#Mock
private EventRepository eventRepository;
#InjectMocks
private EventService eventService;
private Event sampleEventFromDb;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldUpdateEventTestAndWithProperTime() throws Exception {
EventDTO eventDTOMock = Mockito.mock(EventDTO.class);
sampleEventFromDb = Event.builder()
.name("name")
.startDateTime(LocalDateTime.now())
.placeName("place")
.description("description")
.publicEvent(true)
.owner(new User())
.build();
Mockito.when(eventRepository.findOneById(anyString())).thenReturn(Optional.of(sampleEventFromDb));
Mockito.when(eventDTOMock.getId()).thenReturn("1");
eventService.updateEvent(eventDTOMock); //NullPointerException
//...
}
}
I got this same error, after trying a lot of things I fixed it by replacing the anyString() method with any()
Try this:
Mockito.when(eventRepository.findOneById(any())).thenReturn(Optional.of(sampleEventFromDb));
Looks like the problem is that initMock is called twice: once by the runner and once by the setUp method. Running the test with the regular runner or removing the initMocks call from the setUp method fixes this problem.
Change anyList(), anyMap()..... to any(). After long debugging it is worked finally.