Can't pass mock to the inject mock service - java

I have service:
#Service
class UserService {
private final Map<AbstractSomeService, CustomEnum> someMap;
public UserService(List<AbstractSomeService> someService) {
someService.forEach(service -> someMap.put(service.getCustomEnum(), service));
}
public void logicExecution() {
//code
}
}
When i am mocking as below: i am getting NullPointer:
#Mock
private SomeService someService; // Service which is extended of AbstractSomeService
#InjectMocks
private UserService userService = new UserService(Collection.singletonList(someService))
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testBrokenJunit() {
userService.logicExecution(); // NULL POINTER HERE (
}
SomeService:
#Service
public class SomeService extends AbstactSomeService() {
public CustomEnum getCustomEnum() {
return CustomEnum.BROKEN_JUNIT_TEST;
}
//logic here
}
Stack trace is quite simple:
java.lang.NullPointerException: Cannot invoke "getCustomEnum()" because "service" is null
StackTrace without constructor initialization:
org.mockito.exceptions.misusing.InjectMocksException:
Cannot instantiate #InjectMocks field named 'UserService' of type '...'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Cannot invoke "java.util.List.forEach(java.util.function.Consumer)" because "someService" is null
at
Caused by: java.lang.NullPointerException: Cannot invoke "java.util.List.forEach(java.util.function.Consumer)" because "someService" is null
at UserService.<init>
P.S.
When i am using real object of UserService, not a mock everything is ok.
But it doesn't work with #InjectMocks

Try the following in your test class (you don't need to initialize UserService with a new instance via the constructor, #InjectMocks will do that for you):
#Mock
private SomeService someService;
#InjectMocks
private UserService userService;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testBrokenJunit() {
userService.logicExecution();
}
If this does not work because you have a List and not a simple SomeService you can do the following:
#Mock
private SomeService someService;
private UserService userService;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userService = new UserService(Collection.singletonList(someService));
}
#Test
public void testBrokenJunit() {
userService.logicExecution();
}

Use #MockBean to create mock objects
import org.springframework.boot.test.mock.mockito.MockBean;
#MockBean
private User user;

Related

Junit Test Pass even when the method called inside may fail

I am creating a Junit Unit Test to check the createAccount method in service that calls Service a helper method. Please find it below.
Service class
public class AccountServiceImpl {
#Autowired
AccountHelper accountHelper;
#Override
public Account createAccount(Account account) throws CustomerNotFoundException {
accountHelper.checkAccountTypeForCustomer(account);
return accountRepository.save(account);
}
}
Helper Class:
public void checkAccountTypeForCustomer(Account acc) throws CustomerNotFoundException {
Boolean customerExists = customerRepository.existsById(acc.getCustomerId());
if(!customerExists) {
throw new CustomerNotFoundException("604", Message.CUSTOMER_NOT_FOUND);
}
}
AccountServiceTest class
#ExtendWith(MockitoExtension.class)
public class AccountServiceTest {
#Mock
private AccountRepository accountRepository;
#Mock
private CustomerRepository customerRepository;
#Mock
private AccountHelper accountHelper;
#InjectMocks
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Account account = Account.builder().
accountType(AccountType.SAVINGS).
openingBalance(BigDecimal.valueOf(3000)).
ifsc("IFSC1").
customerId(1).
build();
testService.createAccount(account);
}
}
Above Test is passing although the customer is not present in the database.
The test is incomplete. But still the statement: testService.createAccount(account);
must fail as per my understanding.
Kindly correct me if I am wrong. I am relatively new to Junit.
However if I place the implementation for checkAccountTypeForCustomer() inside the service method instead of in the helper, the test case fails as expected.
The reason is that accountHelper is mocked in your test, which means that invocation of accountHelper.checkAccountTypeForCustomer(account) doesn't execute your business code.
I recommend you to use Spring mocking in this case, and to specify how your repository is expected to behave. It would look something like this:
#ExtendWith(SpringExtension.class)
class AccountServiceTest {
#MockBean
private CustomerRepository repository;
#Autowired
private AccountService testService;
#Test
void testCreateAccount() throws CustomerNotFoundException {
Mockito.when(repository.existsById(anyInt())).thenReturn(false);
...
CustomerNotFoundException thrown = Assertions.assertThrows(CustomerNotFoundException.class, () -> testService.createAccount(account));
Assertions.assertEquals("the exception message", thrown.getMessage());
}
}

Can I inject mocks into a prototype bean with Autowired constructor?

Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}

spring controller test by mock service

Love Spring Testing Even More With Mocking and Unit Test Assistant:
A mocked service replaces multiple dependencies
enter image description here
#Controller
#RequestMapping("/people")
public class PeopleController {
#Autowired
protected PersonService personService;
#GetMapping
public ModelAndView people(Model model) {
for (Person person: personService.getAllPeople()) {
model.addAttribute(person.getName(), person.getAge());
}
return new ModelAndView("people.jsp", model.asMap());
}
}
private MockMvc mockMvc:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
private MockMvc mockMvc;
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
I get a mistake when I want to run mockMvc
java.lang.NullPointerException
Perform the following steps:
create service mock instead of service original
("PersonServiceMock")
replace service original by service mock
#Autowired
PersonService personService;
#Autowired
PeopleController peopleController;
private MockMvc mockMvc;
#Before
public void setup() {
peopleController = new PeopleController(new personServiceMock());
mvc = MockMvcBuilders.standaloneSetup(peopleController).build();
}
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
That's because you are never initialising mockMvc in your code and the point where you access it results in nullPointerException. You need to initialise it before using it, and since multiple tests in your class could be using it, best place to do it is setup() method annotated with #before. Try below:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class PeopleControllerTest {
#Autowired
PersonService personService;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
#Configuration
static class Config {
// Other beans
#Bean
public PersonService getPersonService() {
return mock(PersonService.class);
}
}
#Test
public void testPeople() throws Exception {
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
}
from the source code I see that the mockMvc doesn't have any value, thats why it hits "java.lang.NullPointerException" for this line of code :
ResultActions actions = mockMvc.perform(get("/people"));
to make it run, I think need to give value to mockMvc first.
by constructor :
#Test
public void testPeople() throws Exception {
mockMvc = new MockMvc();
// When
ResultActions actions = mockMvc.perform(get("/people"));
}
or Autowired :
#Autowired
MockMvc mockMvc
depends on the purpose of MockMvc Class

Test services in Mockito

I want to test service
public class UserServiceTest {
#Autowired
private UserServiceImpl userService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testExistsUsername() {
User user = userService.findByUsername("jonki97").get();
}
}
However, during the test he throws me out
java.lang.NullPointerException
at com.service.service.UserServiceTest.testExistsUsername(UserServiceTest.java:32)
My service still contains repository
#Service("userService")
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
#Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
It may be that I missed something wrong and therefore throws null.
You do not have any mocks instantiated, even you have this method for initiating mocks. You need a mock of the repository you are calling in your test class:
#Mock
UserRepository userRepository;
And as well to provide the behavior of the mock (what to do when the method is called). Something like:
when(userRepository.findByUsername("jonki97")).thenReturn("OK");
// test the method after
Hope it helps
Have you tried using Runner
#RunWith(SpringRunner.class)
This works for me:
#RunWith(SpringRunner.class)
public class UserServiceTest {
#Mock
UserRepository userRepository;
#Before
public void setup(){
when(userRepository.findByUsername("jonki97")).thenReturn("jonki97");
}
#Test
public void testExistsUsername() {
String user = userRepository.findByUsername("jonki97");
assertEquals("jonki97", user);
}
}

Spring #Value field injection in mockito

#Service
public class MyClass {
...
#Autowired
public MyClass(#Value("${my.value}") String myValue) {
...
}
...
}
My Test class:
public class MyTest {
#Mock(name = "solrServer")
private SolrServer solrServer;
#InjectMocks
private MyClass myClassMock;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
....
}
I have an exeption as follow:
org.mockito.exceptions.base.MockitoException: Cannot instantiate
#InjectMocks field named 'myClassMock' of type 'class
mypackage.MyClass'. You haven't provided the
instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception
: null
at mypackage.MyTest.setUp(MyTest.java:46)
...
Caused by: java.lang.NullPointerException
...
How to inject #Value field from MyTest class, and fixed it?
You can do it within the #Before annotated method by making an instance of your class manually, like so:
public class MyTest {
#Mock(name = "solrServer")
private SolrServer solrServer;
#InjectMocks
private MyClass myClassMock;
#Before
public void setUp() {
myClassMock = new MyClass("value you need");
MockitoAnnotations.initMocks(this);
}
#Test
....
}

Categories

Resources