I am trying to create a test case for AlbumsController which fetches data from the network and parses it.
AlbumService: Responsible for network call and getting data. ( Assume it can be anything that performs some async task and gives callback accordingly so as to avoid "Server" specific mocking solutions )
public class AlbumsController {
public void getAlbums(final ServiceCallback<AlbumsWrapper> albumsServiceCallback) {
new AlbumsService().fetchAlbums(new ServiceCallback<NetworkResponse>() {
#Override
public void onSuccess(NetworkResponse response) {
// parse response
}
#Override
public void onFailure(NetworkResponse error) {
// do something for Failure
}
});
}
public class AlbumControllerTest {
#Mock
private ServiceCallback<NetworkResponse> serviceCallback;
#Captor
private ArgumentCaptor<ServiceCallback<AlbumsWrapper>> albumsWrapper;
#Captor
private ArgumentCaptor<ServiceCallback<NetworkResponse>> networkResponseArgumentCaptor;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void parseAlbums_EmptyList_ReturnsTrue() {
// below are different ways I am trying with no success so far.
AlbumsController albumsController = new AlbumsController();
albumsController.getAlbums(albumsWrapper.capture());
NetworkResponse response = new NetworkResponse();
networkResponseArgumentCaptor.capture();
networkResponseArgumentCaptor.getValue().onSuccess(response);
}
}
Aim:
To mock AlbumService inside getAlbums method so that instead of getting data from the server I should be able to call onSuccess() with my test data as an argument. And is it possible to get the list after parsing so as that I can assert base on the data in the list?
I don't want to move code responsible for parsing to some method and make that public. My intention here is to understand how to handle such a case.
Open for refactoring of code if that's what is required to be taken care of in TDD.
Full solution or pointers to look for both are appreciated.
Libraries
mockito-core:2.2.9
junit:4.12
If small refactoring is the option then:
1) Move the new AlbumsService() to a package level method:
AlbumService createAlbumService(){
return new AlbumService();
}
...
public void getAlbums(final ServiceCallback<AlbumsWrapper> albumsServiceCallback) {
createAlbumService().fetchAlbums(new ServiceCallback<NetworkResponse>()
package visibility is enough as the test class will be in the same package as the AlbumController.
2) Spy the AlbumController:
#Spy
private AlbumsController albumsControllerSpy = new AlbumController();
#Mock
private AlbumService albumServiceMock;
3) Make the createAlbumService() method return your mock:
#Test
public void parseAlbums_EmptyList_ReturnsTrue() {
doReturn(albumServiceMock).when(albumControllerSpy).createAlbumService();
...
Related
I am currently working on a AWS Lambda using Java 11. It is requiring me for an implementation of the handler to have an empty constructor. My handler looks like this
public class ApiKeyHandler {
private final SecretsManagerClient secretsManagerClient;
public ApiKeyHandler() {
secretsManagerClient = DependencyFactory.secretsManagerClient();
}
public void handleRequest(Object event, Context context) {
//Other codes here
secretsManagerClient.getSecret(/../);
}
}
And Dependency Factory class
public class DependencyFactory {
private DependencyFactory() {}
/**
* #return an instance of SecretsManagerClient
*/
public static SecretsManagerClient secretsManagerClient() {
return SecretsManagerClient.builder()
.region(/**/)
.build();
}
}
Now, when I am trying to write unit test for this I cant mock objects in constructor. Is there a way I can mock it?
I tried
#Mock SecretsManagerClient secretsManagerClient;
#InjectMocks ApiKeyHandler handler;
but no luck. Thank you
It looks like you have a couple of options:
You can add another constructor with the parameters to inject. This is easy and clean from the test perspective, but after all you'll have the code in production (this constructor in this case) that is used only for tests.
In generally I don't advocate this approach, although I understand that there are technology limitations here.
You can Mock the Dependency Factory. Since the call is static, you might end up using PowerMock / PowerMockito that can actually mock static calls. This is something that can turn to be really painful to maintain, in general this approach is discouraged these days.
You can Rewrite the DependencyFactory so that it could be configured with some kind of mock implementation (that will allow to specify mock dependencies):
public interface DependencyFactoryMode {
SecretsManagerClient secretsManagerClient();
}
public class RealDependencyFactoryMode implements DependencyFactoryMode {
public SecretsManagerClient secretsManagerClient() {
return SecretsManagerClient.builder()
.region(/**/)
.build();
}
}
// in src/test/java - test code in short
public class DependencyFactoryTestMode implements DependencyFactoryMode {
private SecretsManagerClient smc = Mockito.mock(SecretsManagerClient.class);
public SecretsManagerClient secretsManagerClient() {
return smc;
}
// this will be used in tests
public SecretsManagerClient getSmcMock() {return smc;}
}
public class DependencyFactory {
private static DependencyFactoryMode mode;
static {
// depending on the configuration, external properties or whatever
// initialize in production mode or test mode
// of course this is the most "primitive" implementation you can probably
// do better
if(isTest) {
mode = new TestDependencyFactoryTestMode();
} else {
// this is a default behavior
mode = new RealDependencyFactoryMode();
}
}
private DependencyFactory() {}
public static DependencyFactoryMode getMode() {
return mode;
}
public static SecretsManagerClient secretsManagerClient() {
return mode.secretsManagerClient();
}
}
With this approach you'll have to pre-configure the dependency factory so that while running in the test it will "know" that it should run in the test mode.
public class Test {
#Test
public void test() {
// DependencyFactoryMode will be used in the test mode
DependecyFactoryMode testMode = DependencyFactory.getMode();
var smc = testMode.secretsManagerClient();
Mockito.when(smc.foo()).thenReturn(...);
}
}
Now this approach suffers from the same drawback as "1" but at least you have a code "only for tests" only in the factory, rather than in all lambda functions (I assume you have many of them, otherwise probably the first approach will be the least of all evils).
Another possible drawback is that the same instance of DependencyFactory (with the shared static mocking mode) will be shared between the tests, so you might end up "reseting" all the relevant mocks after the test.
Again, these all are complications because in the form that you've presented there is no way to provide a dependency injection in constructor because of the technology limitation.
Add a second constructor that accepts parameters:
public ApiKeyHandler(SecretsManagerClient client) {
secretsManagerClient = client;
}
public ApiKeyHandler() {
this(DependencyFactory.secretsManagerClient());
}
I have the following Java method:
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = new ESBUrlHelper().getEsbUrl();
AppointmentClient appointmentClient = AppointmentClientFactory.getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
The method above makes a call to a third party REST client called appointmentClient.
The issue that I am having is that this is causing my test to fail.
How can I mock the appointmentClientobject within my unit tests?
Currently my test looks as follows:
#Test
public void shouldAddAppointment() {
// act
Appointment appointment = appointmentService.addAppointment(CLIENT_STRING, appointmentMock)
// assert
assertNotNull(appointment);
}
But I get the following error at line appointmentClient.addAppointment(client, appointment);:
org.jboss.resteasy.client.exception.ResteasyIOException: IOException
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
I want to mock something like as follows:
Mockito.when(appointmentClient.addAppointment(client, appointment)).thenReturn(appointmentMock);
You can try using PowerMockito for this.
First, you need to mock the static method call of AppointmentClientFactory class like below:
PowerMockito.mockStatic(AppointmentClientFactory.class);
PowerMockito.when(AppointmentClientFactory,"getUnsecuredClient",esbUrl).thenReturn(appointmentClient);
And also, when you use PowerMockito for mocking static methods, add the #PrepareForTest({AppointmentClientFactory.class}) annotation to the test class.
With your current code, the only way of mocking a call to AppointmentClientFactory#getUnsecuredClient would be using PowerMock, since the factory method is static. This is due to the hard coupling between your calling code addAppointment and the dependency here (i.e. the AppointmentClientFactory).
If I were you, I would avoid that as PowerMock is not the best way to do tests. Instead, what I would do, would be to inject the AppointmentClientFactory as a dependency thus allowing me to mock an instance of it during my tests.
This should be the best approach in twofold manner. Firstly, because you achieve less tightly coupled code and secondly because you do not need to use PowerMock for your unit-tests.
It's not impossible with Mockito. But your original problem is static method of AppointmentClientFactory. You definitely should change this method to the instance method (at least for better architecture) , for example:
public class AppointmentClientFactory {
public AppointmentClient getUnsecuredClient(String url) {
return new AppointmentClient(); //your implementation
}
}
Then your AppointmentService will look like (or close to it):
public class AppointmentService {
private final AppointmentClientFactory factory;
public AppointmentService() {
this(new AppointmentClientFactory());
}
public AppointmentService(AppointmentClientFactory factory) {
this.factory = factory;
}
public Appointment addAppointment(String client, Appointment appointment) {
String esbUrl = "";
Appointment outputAppointment = null;
AppointmentClient appointmentClient = new AppointmentClientFactory().getUnsecuredClient(esbUrl);
if (appointment.getId() == null) {
outputAppointment = appointmentClient.addAppointment(client, appointment);
}
return outputAppointment;
}
}
And then you will could write test like:
public class AppointmentTest {
private final String CLIENT_STRING = "";
#Test
public void shouldAddAppointment() {
AppointmentClientFactory clientFactory = Mockito.mock(AppointmentClientFactory.class);
AppointmentClient mockedClient = Mockito.mock(AppointmentClient.class);
AppointmentService service = new AppointmentService(clientFactory);
Appointment appointmentMock = new Appointment();
when(clientFactory.getUnsecuredClient(any())).thenReturn(mockedClient);
Appointment appointment = service.addAppointment(CLIENT_STRING, appointmentMock);
assertNotNull(appointment);
}
}
I want start write unit test in my project. I tried make this many times. And he always quit, because he could not understand the meaning. because I can not find and form knowledge into a single whole. I read many articles, saw many examples, and they are all different. As a result, I understand why I need write tests, I understand how to write them, but I do not understand how correctly. And I do not understand how to write them so that they are useful. I have some questions:
For example I have service:
#Service
public class HumanServiceImpl implements HumanService {
private final HumanRepository humanRepository;
#Autowired
public HumanServiceImpl(HumanRepository humanRepository) {
this.humanRepository = humanRepository;
}
#Override
public Human getOneHumanById(Long id) {
return humanRepository.getOne(id);
}
#Override
public Human getOneHumanByName(String firstName) {
return humanRepository.findOneByFirstName(firstName);
}
#Override
public Human getOneHumanRandom() {
Human human = new Human();
human.setId(Long.parseLong(String.valueOf(new Random(100))));
human.setFirstName("firstName"+ System.currentTimeMillis());
human.setLastName("LastName"+ System.currentTimeMillis());
human.setAge(12);//any logic for create Human
return human;
}
}
And I tried write Unit test for this service:
#RunWith(SpringRunner.class)
public class HumanServiceImplTest {
#MockBean(name="mokHumanRepository")
private HumanRepository humanRepository;
#MockBean(name = "mockHumanService")
private HumanService humanService;
#Before
public void setup() {
Human human = new Human();
human.setId(1L);
human.setFirstName("Bill");
human.setLastName("Gates");
human.setAge(50);
when(humanRepository.getOne(1L)).thenReturn(human);
when(humanRepository.findOneByFirstName("Bill")).thenReturn(human);
}
#Test
public void getOneHumanById() {
Human found = humanService.getOneHumanById(1L);
assertThat(found.getFirstName()).isEqualTo("Bill");
}
#Test
public void getOneHumanByName() {
Human found = humanService.getOneHumanByName("Bill");
assertThat(found.getFirstName()).isEqualTo("Bill");
}
#Test
public void getOneHumanRandom() {
//???
}
}
I have questions:
1. Where should I fill the objects? I saw different implementations
in #Before like in my example, in #Test, mix implementations - when Human create in #Before and expression
when(humanRepository.getOne(1L)).thenReturn(human);
in #Test method
private Human human;
#Before
public void setup() {
human = new Human();
...
}
#Test
public void getOneHumanById() {
when(humanRepository.getOne(1L)).thenReturn(human);
Human found = humanService.getOneHumanById(1L);
assertThat(found.getFirstName()).isEqualTo("Bill");
}
2. How can I test getOneHumanRandom() method?
Service not use repository when call this method. I can make this method mock, but what will it give?
when(humanService.getOneHumanRandom()).thenReturn(human);
...
#Test
public void getOneHumanRandom() {
Human found = humanService.getOneHumanRandom();
assertThat(found.getFirstName()).isEqualTo("Bill");
}
I just copy the logic from the service in the test class. What is the point of such testing and is it necessary?
1. Where should I fill the objects? I saw different implementations
I would use #Before for any common setup between all / most tests. Any setup that is specific to a certain test should be go into that test method. If there is a common setup between some, but not all, of your tests you can write private setup method(s).
Remember to keep your tests / code DRY (dont repeat yourself!). Tests have a maintenance factor and keeping common code together with help alleviate some headaches in the future.
2. How can I test getOneHumanRandom() method?
You should create a Human toString() method. This method should concat all the properties on your Human object. You can call getOneHumanRandom() twice and assert that they are not equal.
#Test
public void getOneHumanRandomTest()
{
// SETUP / TEST
Human one = service.getOneHumanRandom();
Human two = service.getOneHumanRandom();
// VERIFY / ASSERT
assertNotEquals("these two objects should not be equal", one.toString(), two.toString())
}
Hope this helps!
I would like to check if a private method of my class to test is executed or not using powermockito.
Suppose that I have this class to test:
public class ClassToTest {
public boolean methodToTest() {
//doSomething (maybe call privateMethod or maybeNot)
return true;
}
//I want to know if this is called or not during the test of "methodToTest".
private void privateMethod() {
//do something
}
}
When I test "methodToTest" I want to check if it returns the correct result but also if it executes the private method "privateMethod" or not.
Searching on other discussion I wrote this test that makes use of powermockito but it doesnt works.
public class TestClass {
#Test
testMethodToTest(){
ClassToTest instance = new ClassToTest();
boolean result = instance.methodToTest();
assertTrue(result, "The public method must return true");
//Checks if the public method "methodToTest" called "privateMethod" during its execution.
PowerMockito.verifyPrivate(instance, times(1)).invoke("privateMethod");
}
}
When I use the debugger it seems that the last line (PowerMockito.verifyPrivate...) doesn't check if the private method has been executed once during the test but instead it seems that it executes the private method itself. Furthermore the test passes but using the debugger I'm sure that the private method is not executed during the call of "instance.methodToTest()".
What is wrong?
I'd do it easier way, without PowerMockito. Consider this (it's some kind of Spy object):
public class TestClassToTest {
private boolean called = false;
#Test
public void testIfPrivateMethodCalled() throws Exception {
//given
ClassToTest classToTest = new ClassToTest() {
#Override
void privateMethod() {
called = true;
}
};
//when
classToTest.methodToTest();
//then
assertTrue(called);
}
}
This requires changing privateMethod() to package-private (but there's nothing wrong with this).
But bear in mind, that testing implementation is a bad practise and can lead to fragile tests. Instead you should test only results.
public class Holder() {
Contact contact1;
Contact contact2;
}
public class ContactServiceImpl() {
public Contact create(Contact contact) {
// do create
}
}
public class HolderServiceImpl() {
ContactService contactService = new ContactServiceImpl();
public Holder createHolder(Holder holder) {
contactService.create(holder.getContact1());
contactService.create(holder.getContact2());
return holder;
}
}
pulbic class HolderServiceTest() {
ContactServiceImpl contactService = new ContactServiceImpl();
HolderServiceImpl holderService = new HolderServiceImpl();
#Before
public void setUp() {
contactService = EasyMock.createMock(ContactServiceImpl.class);
holderService.setContactServiceImpl(contactService);
EasyMock.expect(contactService.create(EasyMock.isA(Contact.class))).andReturn(new Contact()).anyTimes();
}
#Test
public void testCreate() {
Holder holder = new Holder();
holderService.create(holder)
}
}
When not setting the replay, I have an error on the second call of contactService.create which is IllegalStateException.
However when adding (After the expect call) :
EasyMock.replay(contactService);
I get this error:
Unexpected method call ContactServiceImpl.create(Contact#4ebd441a[
I tried using PowerMock but I get the same issue. Is it even possible to have both calls like that? Anytimes() seems to exist for that use but it's not working much.
You never did put the mock in replay state. Just add EasyMock.replay(contactService); at the end of your setUp method.
Actually what was wrong is that my method testCreate was missing the setters on contact1 and contact2 which caused the error.
I had the option of either:
Setting both objects
Using EasyMock.isNull(Contact.class) instead of EasyMock.isA(Contact.class).
However since it was possible for in real cases to have either one of them as null, the best solution was to use EasyMock.anyObject(Contact.class) which accepts both initiated objects or nulls.
#Before
public void setUp() {
contactService = EasyMock.createMock(ContactServiceImpl.class);
holderService.setContactServiceImpl(contactService);
EasyMock.expect(contactService.create(EasyMock.anyObject(Contact.class))).andReturn(new Contact()).anyTimes();
}