Unable to mock certain final classes with PowerMockito - java.lang.IllegalAccessError - java

When I try to mock certain final classes like SearchGoogleAdsRequest from a certain Google library they give me IllegalAccessError , I have added #PrepareForTest annotation on top of the class.
#RunWith(PowerMockRunner.class)
#PrepareForTest({SearchGoogleAdsRequest.class})
#PowerMockIgnore({"javax.net.ssl.*"})
public class GoogleAdsReportDownloaderTest {
final SearchGoogleAdsRequest searchGoogleAdsRequest = PowerMockito.mock(SearchGoogleAdsRequest.class);
final GoogleAdsServiceClient mockGoogleAdsServiceClient = mock(GoogleAdsServiceClient.class);
final GoogleAdsServiceClient.SearchPagedResponse searchPagedResponse =
mock(GoogleAdsServiceClient.SearchPagedResponse.class);
when(mockGoogleAdsServiceClient.searchPagedCallable()).thenReturn(callable);
when(mockGoogleAdsServiceClient.searchPagedCallable().call(searchGoogleAdsRequest)).thenReturn(searchPagedResponse);
when(searchPagedResponse.iterateAll()).thenReturn(Arrays.asList(mockGoogleAdsRow));
when(mockGoogleAdsServiceClient.search(any())).thenReturn(searchPagedResponse);
Error
java.lang.IllegalAccessError: Class com/google/ads/googleads/v6/services/SearchGoogleAdsRequest$MockitoMock$1353664588 illegally accessing "package private" member of class com/google/protobuf/GeneratedMessageV3$UnusedPrivateParameter
SearchGoogleAdsRequest final class looks like this, which PowerMockito isn't able to mock.
public final class SearchGoogleAdsRequest extends GeneratedMessageV3 implements SearchGoogleAdsRequestOrBuilder {
private static final SearchGoogleAdsRequest DEFAULT_INSTANCE = new SearchGoogleAdsRequest();
private static final Parser<SearchGoogleAdsRequest> PARSER = new AbstractParser<SearchGoogleAdsRequest>() {
public SearchGoogleAdsRequest parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException {
return new SearchGoogleAdsRequest(input, extensionRegistry);
}
};
private SearchGoogleAdsRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
super(builder);
this.memoizedIsInitialized = -1;
}
private SearchGoogleAdsRequest() {
this.memoizedIsInitialized = -1;
this.customerId_ = "";
this.query_ = "";
this.pageToken_ = "";
this.summaryRowSetting_ = 0;
}
I am able to bypass this , by configuring MockMaker. But MockMaker doesn't work with PowerMockito and gives errors (cannot initialize plugin and that certain loader was supposed to go with Mockito but is loading with PowerMockito).
I need to use Power Mockito because I need to mock local scope objects and other unit tests created by others break with MockMaker.

I resolved this using newBuilder to create an object of the class and bypass the issue. It doesn't really mock the class, but I could use this in my case as an argument to a class which I needed to mock. If we don't require PowerMockito then we can use Mockito along with MockMaker, where these classes can be easily mocked though.
final SearchGoogleAdsRequest searchGoogleAdsRequest = SearchGoogleAdsRequest.newBuilder().build();
final GoogleAdsServiceClient mockGoogleAdsServiceClient = mock(GoogleAdsServiceClient.class);
final GoogleAdsServiceClient.SearchPagedResponse searchPagedResponse =
mock(GoogleAdsServiceClient.SearchPagedResponse.class);
when(mockGoogleAdsServiceClient.searchPagedCallable()).thenReturn(callable);
when(mockGoogleAdsServiceClient.searchPagedCallable().call(searchGoogleAdsRequest)).thenReturn(searchPagedResponse);
when(searchPagedResponse.iterateAll()).thenReturn(Arrays.asList(mockGoogleAdsRow));
when(mockGoogleAdsServiceClient.search(any())).thenReturn(searchPagedResponse);

Related

Mockito - how to mock a variable instantiated in a method?

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

Mockito test in MVP pattern

I'm trying to unit test my Presenter class using Mockito and I always end up failing the test:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.String
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
This is what my Presenter class looks like:
public class EditorPresenter implements EditorContract.Presenter {
private DataSource dataSourceImpl;
private EditorContract.View mView;
private SharedPreferences prefs;
EditorPresenter(SharedPreferences prefs,
DataSourceImpl dataSourceImpl,
EditorContract.View mView) {
this.dataSourceImpl = dataSourceImpl;
this.mView = mView;
this.prefs = prefs;
mView.setPresenter(this);
}
#Override
public void showNewNote() {
String noteColor = prefs.getString("default_note_color", "#ef5350");
String textColor = prefs.getString("default_text_color", "#000000");
mView.noteColor(Color.parseColor(noteColor));
mView.textColor(Color.parseColor(textColor));
}
}
And this is what I've done so far in EditorPresenterTest class:
public class EditorPresenterTest {
#Mock
private EditorContract.View mView;
#Mock
private DataSourceImpl mDataSourceImpl;
#Mock
private SharedPreferences sharedPrefs;
#Mock
private String noteColor;
#Mock
private String textColor;
#Before
public void setUpEditorPresenter() {
MockitoAnnotations.initMocks(this);
}
#Test
public void createEditorPresenter_newNote() {
EditorPresenter editorPresenter = new EditorPresenter(
sharedPrefs,
mDataSourceImpl,
mView);
verify(mView).setPresenter(editorPresenter);
}
#Test
public void showNewNote() {
when(sharedPrefs.getString(eq("default_note_color"), eq("#ef5350"))).thenReturn(noteColor);
when(sharedPrefs.getString(eq("default_text_color"), eq("#000000"))).thenReturn(textColor);
verify(mView).textColor(Color.parseColor(noteColor));
verify(mView).noteColor(Color.parseColor(textColor));
}
(Note: I'm new to Mockito and testing)
I have passed the createEditorPresenter_newNote() but the showNewNote() failed the test and shows error. Any Feedback/Answers are welcome. Hope someone helps me. Thanks!
I will first answer the exact question that you asked here but keep in mind that you have a larger issue that is hiding behind your compilation error, for which I will provide an answer right after.
(please keep in mind that I have no real experience with Android, so exact class names and use cases might not be valid, but your issues are more with understanding what test frameworks do and not syntax-oriented).
Your first issue is that you are trying to create mock types of the String class, which is final. As you can see in the error from Mockito:
Mockito cannot mock/spy following:
- final classes
In essence, there is no real reason for creating a mock of a String, because you are not testing String functionality. You can just use a constant.
if that is what you wish to fix, just remove the #Mock annotations from the noteColor and textColor variables and initialize them with some constant values.
More about testing frameworks and the other problems you are facing:
There is another major issue in your test case, and that is that you are trying to use the EditorPresenter you created in the first test inside the scope of the second test.
The thing is that test frameworks run different tests in separate states (rightfully so). So when you create the EditorPresenter instance inside the createEditorPresenter_newNote method, it won't be visible for you in the showNewNote test method, because it is a different process (not a different CPU process - just a process in the simple day-to-day term of the word).
What should you be doing instead?
That's what the before method is for: it will be called before every test runs, so you can set up shared functionality in one place.
what you should be doing is more on the line of this:
public class EditorPresenterTest {
#Mock
private EditorContract.View mView;
#Mock
private DataSourceImpl mDataSourceImpl;
#Mock
private SharedPreferences sharedPrefs;
private EditorPresenter editorPresenter;
#Before
public void setUpEditorPresenter() {
MockitoAnnotations.initMocks(this);
this.editorPresenter = new EditorPresenter(
sharedPrefs,
mDataSourceImpl,
mView);
}
#Test
public void createEditorPresenter_newNote() {
verify(mView).setPresenter(editorPresenter);
}
#Test
public void showNewNote() {
editorPresenter.showNewNote();
String noteColor = "#ef5350"; // or whatever color you want
String textColor = "#000000"; // or whatever color you want
when(sharedPrefs.getString(eq("default_note_color"), eq("#ef5350"))).thenReturn(noteColor);
when(sharedPrefs.getString(eq("default_text_color"), eq("#000000"))).thenReturn(textColor);
verify(mView).textColor(Color.parseColor(noteColor));
verify(mView).noteColor(Color.parseColor(textColor));
}
}

Using factory method for creating common Mockito stubbings in Java

In project I am working on we have a bunch of commonly used helpers. Consider the following example:
public class ServiceHelper {
public HttpServletRequest() getRequest() { ... }
public Model getModel() { ... }
public UserCache getUserCache() { ... }
public ComponentContainer getComponentContainer() { ... }
}
Imagine this helper is being used across the whole application by every web service we have. Then, in order to test these services I need to mock it. Each time. But what if I create a factory of some kind instead, something like:
public class ServiceHelperMockStore {
public static ServiceHelper create() {
return init();
}
public static ServiceHelper create(final Model model) {
final ServiceHelper helper = init();
when(helper.getModel()).thenReturn(model);
return helper;
}
private static ServiceHelper init() {
final ServiceHelper helper = mock(ServiceHelper.class);
final HttpServletRequest request = mock(HttpServletRequest.class);
final Model model = mock(Model.class);
final UserCache userCache = mock(UserCache.class);
final ComponentContainer container = mock(ComponentContainer.class);
final BusinessRules businessRules= mock(BusinessRules.class);
final ModelTransformer modelTransformer = mock(ModelTransformer.class);
when(helper.getRequest()).thenReturn(request);
when(helper.getModel()).thenReturn(model);
when(helper.getUserCache()).thenReturn(userCache);
when(helper.getComponentContainer()).thenReturn(container);
when(container.getComponent(BusinessRules.class)).thenReturn(businessRules);
when(componentContainer.getComponent(ModelTransformer.class)).thenReturn(modelTransformer);
return helper;
}
}
This factory nicely fit my purposes and oftentimes I can completely avoid using 'mock' and 'when' in the actual test suites. Instead, I can do the following:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ModelServiceTest {
private final Model model = new Model();
private final ServiceHelper serviceHelper = ServiceHelperMockStore.create(model);
private final BusinessRules businessRules = serviceHelper.getComponentContainer().getComponent(BusinessRules.class);
private final ModelType modelType1 = new ModelType();
private final ModelType modelType2 = new ModelType();
private final ModelService modelService = new ModelService(serviceHelper);
#Before
public void setUp() {
modelType1.setItemId("item1");
modelType2.setItemId("item2");
model.setTypes(modelType1, modelType2);
when(businessRules.get("type")).thenReturn(modelType1);
}
...tests...
}
So instead of creating a lot of mocks in the ModelServiceTest, I can just access the predefined ones, like:
BusinessRules businessRules = serviceHelper.getComponentContainer().getComponent(BusinessRules.class);
and this even reflect my helper's API. Also, I can provide my own mock or stub passing parameters to my factory method or using some different approach.
The only problem I have is UnnecessaryStubbingException being thrown by Mockito as normally I don't use all those stubbings I've created per each test file. So I have to use MockitoJUnitRunner.Silent runner to silent the error and according to the mockito api docs it is not recommended.
So I am seeking for an advice what kind of approach must be chosen in this case. Am I doing it right or there is some other way? Or, maybe, using such kind of factories is a bad style of programming in relation to unit tests as it hides some initialization and makes happening things less evident so I must do just a plain copy of my code between test suits?
The fact that you need this identical complex mock configuration at different places shows that your code violates the Law of Demeter (Don't talk to strangers).
A unit should only get dependencies it actually interacts with (other than only to getting another dependency from it).
So instead of creating a lot of mocks in the ModelServiceTest, I can just access the predefined ones,
You Unittests are not only verification of correct behavior but also minimal examples how to use the CUT (Code under test).
The configuration of the CUTs dependencies is an essential part of that example and should be easily accessible to the reader of the tests.
I'd strongly discourage from "factories for mocks" especially it they were moved to other classes (in the test folder).

Implementing a class of "constants" initialized at application start not at compile time

I'm working on a Java project that uses a big class of constants like:
public final class Settings {
public static final int PORT_1 = 8888;
public static final int PORT_2 = 8889;
...
}
Now, some of the value of those constants are not available at compile time anymore so I need a way to "initialize" them at application starts (e.g. from the args[]). Once initialized there should be no way to change them. I'm not very skilled in java, how do I do this in an acceptable way?
I thought of using a singleton with something like a "one shot" set method that throws an exception if called more than one time but it seams too hacky...
You can use a static initializer like this:
public final class Settings {
public static final int PORT_1;
public static final int PORT_2;
...
static {
// create the value for PORT_1:
PORT_1 = ...;
// create the value for PORT_2:
PORT_2 = ...;
}
}
The static initializer is executed during class loading. The final keywords on PORT_1 and PORT_2 protects them to be changed afterwards.
Well, using system properties is a way of doing it unless there is a huge amount of constants.
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
System properties are passed on the command line when starting the application using the -D flag.
If there are too many variables a static initializer can be used where a property file or similar can be read that holds the properties:
public class Constants {
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
private static final String CONSTANT3;
private static final String CONSTANT4;
static {
try {
final Properties props = new Properties();
props.load(
new FileInputStream(
System.getProperty("app.properties.url", "app.properties")));
CONSTANT3 = props.getProperty("my.constant.3");
CONSTANT4 = props.getProperty("my.constant.3");
} catch (IOException e) {
throw new IllegalStateException("Unable to initialize constants", e);
}
}
}
Note that if you are using some external framework such as Spring Framework or similar there is usually a built-in mechanism for this. E.g. - Spring Framework can inject properties from a property file via the #Value annotation.
There is no simple way to do this in Java. One way to simulate this is to use a builder which returns an internal type (so it can write the private fields) but the internal type only has getters.
See this answer: https://stackoverflow.com/a/1953567/34088

Constructor not defined during Test

I am having a pesky error testing this piece of code.Not sure what I am doing wrong. I have tested standard controllers before but not the getSelectedMethod
Error:
Constructor not defined:[myClass].(ApexPages.StandardController)
public class MyClass{
public List<Web__c> postings {get;set;}
public static final String POSTINGSTATUS = 'Yes';
public MyClass (ApexPages.StandardSetController controller) {
List<Web__c> selectedWeb = (List<Web__c>) controller.getSelected();
postings = [Select Id, Name,
FROM Web___c Where Id IN: selectedWeb
AND Date__c != null
AND Date__c > today];
}
}
My Test Class
private class myTestClass {
static testMethod void WebTest() {
List <Web__c> posting = helper.createWeb(4);
insert posting;
Test.startTest();
PageReference pageRef = Page.VFPAGE;
Test.setCurrentPage(pageRef);
ApexPages.StandardController sc = new ApexPages.standardController(posting[0]);
myClass JPC = new myClass(sc);
sc.setSelected(posting);
Test.stopTest();
}
new ApexPages.standardController(posting[0]) looks wrong to me.
It should likely be new ApexPages.StandardSetController(posting[0]) if you want to create a new instance of ApexPages.StandardSetController, or just ApexPages.standardController(posting[0]) (without the new) if it's a static method that returns such an instance.
BTW: you should state what error you get and where in the code it occurs when asking such a question, as figuring it out without that information is pretty hard. I just found this by chance, I'd asked for that information otherwise.

Categories