Spring WebFlux: Unit Testing subscribe function of Mono - java

I have the following classes/interfaces:
public interface Api {
Mono<Object> update();
}
#Service
public class Service {
#Autowired
private Api api;
private List<Object> list;
public void update() {
api.update().subscribe(result -> list.add(result), Throwable::printStackTrace);
}
}
I'm trying to perform a unit test using Mockito of the consumer function when I subscribe to the Mono that is being returned by my Api. So far, my test class looks as follows
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#Mock
private Mono<Object> mono;
#MockBean
private Api api;
#Autowired
private Service service;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpdate() {
when(api.update()).thenReturn(mono);
doAnswer((Answer<Void>) invocation -> {
System.out.println("FOO");
Consumer<Object> consumer = invocation.getArgument(0);
consumer.accept(testObject)
return null;
}).when(mono).subscribe(any(Consumer.class), any(Consumer.class));
service.update();
}
}
First, I am seeing that the code inside doAnswer() never gets invoked. Secondly, since Mockito's any() returns null and the error consumer of subscribe function expects non-null I'm running into NullPointerException when I run my test.
What would be the best approach of going about unit testing my subscribe's consumer function?

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());
}
}

Unable to mock a method which takes an interface

I have a service in my Spring Boot Project in which i have method which takes an interface.
interface IT {}
class AService {
public String method(IT it) {}
}
I have two classes which implements that interface.
class AIT implements IT {}
class BIT implements IT {}
I am using this service method in some other service passing the AIT/BIT class object according to my need.
Now, I am writing the test cases for other service mocking the Service
public class OtherServiceTests {
#MockBean
private Service service;
#Before
public void setUp() {
// none of these mocks working
Mockito.when(service.method(Mockito.any()))
.thenReturn("");
Mockito.when(service.method(Mockito.any(IT.class)))
.thenReturn("");
Mockito.when(service.method(Mockito.any(BIT.class)))
.thenReturn("");
Mockito.when(service.method(Mockito.any(AIT.class)))
.thenReturn("");
// all returing to NullPointerException
otherService = new OtherSerice();
}
}
None of these mocks are working for this method only. Other mocks are working fine. It is returning NullPointerException which makes the tests fail.
I am new to testing using mockito. If anyone can guide me for this solution than this will be very helpful for me.
Mock is not initialized and the annotation #MockBean should be replaced with #Mock.
Try to change it like this:
public class AServiceTest {
#Mock
AService service;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void myTest(){
Mockito.when(service.method(Mockito.any())).thenReturn("");
assertEquals("", service.method(new AIT()));
}
}

First mock of the method applied always

I want to test a few cases in a method by mocking external dependency to return different results for every test case. But when always returns what is defined at first time (in this example - empty set) and that brokes the next tests.
If I run tests one by one they pass successfully but when I run the whole class only the first test pass and others fail.
Testing class:
class ExampleTest {
#Mock
private Dao dao;
#Mock
private Validator validator;
#Spy
#InjectMocks
Controller controller;
#BeforeEach
void setUp() {
initMocks(this);
}
private final static Set DATA = Set.of("data1", "data2");
#Test
void firstTest() throws UserDashboardException, DashboardException, WidgetException {
when(validator.filter(DATA)).thenReturn(Collections.emptySet());
assertThrows(Exception.class, () -> controller.create(DATA));
}
#Test
void secondTest() throws UserDashboardException, DashboardException, WidgetException {
when(validator.filter(DATA)).thenReturn(DATA);
controller.create(DATA);
verify(dao, times(1)).create(eq(DATA));
}
}
Tested class:
public class Controller {
private Dao dao;
private Validator validator;
public Controller(Dao dao,Validator validator) {
this.dao = dao;
this.validator = validator;
}
public String create(Set<String> data) {
data = validator.filter(data);
if (data.isEmpty()) {
throw new Exception("Invalid data.");
}
return dao.create(data);
}
}
So, in both tests create method throws an exception which is not what I expect. Maybe I miss some point?
Have you tried with doReturn method?
doReturn(DATA).when(validator).filter(DATA)
which can be import from org.mockito.Mockito.doReturn;
Edited: there might be a bug inside your code implementation:
data = validator.filter(data);

Injection of mocks doesn't work with when clauses

I have to test the following class, with an autowired object:
public class Provider {
#Autowired
private Service service;
public Provider() {}
public Provider(final Service service) {
this.service = service;
}
// Other code here
}
I created my test as follows:
#RunWith(PowerMockRunner.class)
public class ProviderTest {
#Mock(name="service") Service service;
#Mock Score score;
#InjectMocks Provider provider = new SearchResultProvider();
#Before
public void setup() {
when(service.process()).thenReturn(score);
}
#Test
public void my_test() {
provider.execute(); // It fails, because service.process() returns null
// Other code here
}
// Other tests here
}
However, when I run the test, it fails. Everything is fine, except the clause when(...) that seems to be ignored.
That causes the test to fail on the call to provider.execute(). Inside this function the call to service.process() is executed and then I would expect a "score mock" to be returned. But a null value is returned instead.
What have I done wrong?
The sample code show no need for powermock, I suppose there's PowerMock because some code is final. But the test don't prepare the classes to be definalized.
I will suppose the code with a final method is the Service
public class Service {
public final Score process() {
throw new NullPointerException("lol");
}
}
Then the code won't pass, unless the test prepares the classes to be definalized :
#RunWith(PowerMockRunner.class)
#PrepareForTest(Service.class)
public class ProviderTest {
...
}

Spring + Mockito injection not working

I'm new with Spring and Mockito testing. I couldn't find answer to my problem on stackoverflow.
I have the following classes:
#EnableScheduling
#Service
public class ServiceEx {
private Queue<Object> tasks = new ConcurrentLinkedQueue();
public void addItem(Object task) {
tasks.add(task);
}
#Scheduled(fixedRate = 30000)
public void executePendingTask() {
tasks.remove();
}
public void drop() {
tasks.clear();
}
public boolean isEmpty() {
return tasks.isEmpty();
}
}
#Controller
#RequestMapping("/drop")
public class ControllerEx {
private ServiceEx service;
#Inject
public ControllerEx(ServiceEx service) {
this.service = service;
}
#RequestMapping(method = RequestMethod.GET)
public String dropTasks(Model model) {
service.drop();
return "redirect:/home";
}
}
And my testing class looks like :
public class ControllerTest {
#Inject
private ServiceEx service;
#InjectMocks
private ControllerEx controller;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void test() {
service.add(task1);
service.add(task2);
this.mockMvc.perform(get("/drop")).andExpect(status().is3xxRedirection());
assertTrue(service.isEmpty());
}
}
My problem is that service is null in both ControlleEx and ControllerTest and i don't want to mock it. I tried several annotations, but didn't find which one to use.
What have i done wrong?
Looking at your code you seem to be mixing unit tests with integration tests.
MockMvc is usually used to do integration tests starting at the controller level. this means that you need a configured and started applicationContext to successfully use that. If that's what this test class is supposed to do then I don't see the use of Mocks, unless you wire them in the application context, your controller won't use them.
If you want to do integration testing, but want to Mock or stub out certain functionality (which in my opinion should only be done because of dependencies on external systems) you should think about wiring some stubs in your applicationContext for this test instead of trying to use Mockito for this.
Also keep in mind that by default, the applicationContext is re-used to run all your tests, which could mean that stubbing for one test could affect an other.

Categories

Resources