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);
Related
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());
}
}
I have a service that has a DataProvider which I want to mock.
Problem: the service uses the data provider in #PostConstruct. But when I use #MockBean, the mocked values are not jet present in #PostConstruct.
What could I do?
#Service
public class MyService {
private List<Object> data;
#Autowired
private DataProvider dataProvider;
#PostConstruct
public void initData() {
data = dataProvider.getData();
}
public void run() {
System.out.println(data); //always null in tests
}
}
#SpringBootTest
public class Test {
#MockBean
private DataProvider dataProvider;
#Test
public void test() {
when(dataProvider.getData()).thenReturn(mockedObjects);
//dataProvider.init(); //this fixes it, but feels wrong
service.run();
}
}
IMHO unit testing MyService would be a better solution for this particular scenario (and I wouldn't feel wrong about calling initService manually in that case), but if you insist...
You could simply override the DataProvider bean definition for this particular test and mock it beforehand, sth like:
#SpringBootTest(classes = {MyApplication.class, Test.TestContext.class})
public class Test {
#Test
public void test() {
service.run();
}
#Configuration
static class TestContext {
#Primary
public DataProvider dataProvider() {
var result = Mockito.mock(DataProvider.class);
when(result.getData()).thenReturn(mockedObjects);
return result;
}
}
}
You might need to set spring.main.allow-bean-definition-overriding to true for the above to work.
I got 2 modules User and Email, both of them have 1 entry point which is a facade, rest is package scoped. The configuration is done in 2 classes
#Configuration
class UserConfiguration {
#Bean
UserFacade userFacade(UserRepository repository, EmailFacade emailFacade) {
return new UserFacade(repository, emailFacade);
}
}
#Configuration
class EmailConfiguration {
#Bean
EmailFacade emailFacade(EmailSender emailSender) {
return new EmailFacade(emailSender);
}
}
Now, I want to write tests that don't require Spring to start. I implemented a simple InMemoryRepository to make this happen
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade = new EmailFacade(new FakeEmailSender());
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I need some fake objects to instantiate EmailFacade so I wrote fake implementation
public class FakeEmailSender implements EmailSender {
#Override
public void sendEmail(EmailMessage emailMessage) throws RuntimeException {
}
}
In that scenario, I'm testing User domain, so I want to mock Email anyways.
I wrote a test to check if it works
#Test
public void shouldReturnSendingFailed() {
Mockito.when(emailFacade.sendUserVerificationEmail(Mockito.any())).thenReturn(Either.left(EmailError.SENDING_FAILED));
assertThat(userFacade.registerNewUser(RegisterUserDto.builder()
.username(USERNAME_4)
.email(VALID_EMAIL)
.password(VALID_PASSWORD).build()).getLeft(), is(EmailError.SENDING_FAILED));
}
But it isn't... after running this test I got
java.util.NoSuchElementException: getLeft() on Right
edit#
regiserNewUser() method
Either<DomainError, SuccessMessage> register(RegisterUserDto registerUserDto) {
if(userRepository.findUser(registerUserDto.getUsername()).isPresent())
return Either.left(UserError.USERNAME_ALREADY_EXISTS);
var userCreationResult = User.createUser(registerUserDto);
var savedUser = userCreationResult.map(this::saveUser);
var emailDto = savedUser.map(this::createVerificationEmail);
return emailDto.isRight() ? emailFacade.sendUserVerificationEmail(emailDto.get())
: Either.left(emailDto.getLeft());
}
Edit2#
With following test configuration
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
#InjectMocks
private UserFacade userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
I got nullpointer here, last line of registerNewUser().
Try running this code
#RunWith(MockitoJUnitRunner.class)
public class RegisterUserTest {
#Mock
private EmailFacade emailFacade;
private UserFacade userFacade;
#Before
public void setUp() {
userFacade = new UserConfiguration().userFacade(new InMemoryUserRepository(), emailFacade);
}
}
There are a few issues with your code:
You initialize your mocks twice. You don’t need to call initMocks in the setUp method if you are using Mockito runner
You are trying to inject mocks to already initialized object. But the field you are trying to inject is also passed to the constructor. Please read #InjectMocks doc, to check the strategies used to inject the mocks:
constructor (not used here, already initialized object)
setter (do you have one?)
field (is it not final)
There are details to each strategy (see my questions above). If no staregy is matched, Mockito will fail silently. The fact that you are passing an object in constructor, and rely on setter or field injection afterwards makes this code unnecesarily complex.
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 {
...
}
I have this code in my Subject Under Test:
public class Widget {
private Set<Thing> things;
public Set<Thing> getThings() { return things; }
public void setThings(Set<Thing> things) { this.things = things; }
public void performAction(PerformingVisitor performer) {
for (Thing thing: getThings())
{
thing.perform(performer);
}
}
}
My JUnit/Mockito test looks like:
#RunWith(MockitoJUnitRunner.class)
public class WidgetTest {
#Mock private PerformingVisitor performer;
#Mock private Thing thing;
#InjectMocks private Widget widget;
#Before
public void setUp() throws Exception {
Set<Thing> things = new HashSet<Thing>();
things.add(thing);
widget.setThings(things);
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldPerformThing() {
Mockito.when(thing.perform(Mockito.any(PerformingVisitor.class))).thenReturn(true);
widget.performAction(performer);
Mockito.verify(thing).perform(Mockito.any(PerformingVisitor.class));
}
}
However, this gives me the error:
Wanted but not invoked:
thing.perform(<any>);
-> at com.myclass.ThingTest.shouldPerformThing(WidgetTest.java:132)
I've verified that the code enters the for loop and should be calling the actual thing.perform(performer); line, but my mock does not seem to be recording a call.
I think you need initMocks before the mock injection.
Could you try to change your setUp method for this:
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Set<Thing> things = new HashSet<Thing>();
things.add(thing);
widget.setThings(things);
}
Hope it works
From the MockitoJUnitRunner javadoc:
Initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary.
So, if you remove
MockitoAnnotations.initMocks(this)
from your setUp method, the test pass.