I have a class with a private method that calls some external class and executes it, how can I prevent this without using powermock? (the project uses Junit5 which at this point does not not support powermock yet).
I considered moving these functions outside, but I feel that sometimes methods really belong in a particular class because they are part of it, and it makes no sense to me to move them out, below is just an example to illustrate.
I already know most people will say not to test private methods, but a. I don't completely agree, and b. I'm not trying to test the method here, just to prevent it executing an external call, even to a fake api url.
public class GenericOauth2Client implements Oauth2Client {
private NetHttpTransport HTTP_TRANSPORT;
private JsonFactory JSON_FACTORY;
public GenericOauth2Client(<some_args>) {
...
HTTP_TRANSPORT = new NetHttpTransport();
JSON_FACTORY = new JacksonFactory();
}
public foo(<some_args>) {
...
fetchRefreshTokenResponse( < some_args >);
...
}
private TokenResponse fetchRefreshTokenResponse(<some_args>)
throws IOException {
return new RefreshTokenRequest(HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(tokenServerUrl),
refreshToken.tokenValue)
.setClientAuthentication(new BasicAuthentication(clientId, clientSecret))
.execute();
}
}
How can this be handled / tested properly?
My approach is usually to make the method protected and create a little helper class for the test which derives from the class under test and override the protected method with a stub implementation.
Make another constructor for it that's not public. Also "program to the interface" so that you can inject behavior more easily
public class GenericOauth2Client implements Oauth2Client {
private HttpTransport transport;
private JsonFactory jsonFactory;
public GenericOauth2Client(<some_args>) {
...
transport = new NetHttpTransport();
jsonFactory = new JacksonFactory();
}
GenericOauth2Client(<some_args>, HttpTransport t) {
this(args);
transport = t;
}
Alternatively, if you have no control over the http client, but can change the url, then WireMock would be the other option to mock the server responses
Related
Below is the method which I wanted to test but as per my knowledge Junit5 doesn't support PowerMockito. So is there any way I can mock private method call inside another method ?
public Class MyClass {
private void sendEmailNotification(Checklist Checklist){
EmailService emailService = new EmailService();
BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
String esName = esDO.getName();
SharedInfo sharedInfo = Checklist.getShared_info();
sharedInfo.setEng_space_name(esName);
String reviewer = Checklist.getReviewer();
String ChecklistUri = Checklist.getUri();
String ChecklistName = Checklist.getName();
String targetPhase = Checklist.getTarget_phase();
String comment = Checklist.getComment();
String submitter = Checklist.getSubmitter();
String appURL = Checklist.getShared_info().getApp_url();
String ChecklistLink = buildChecklistURL(appURL, ChecklistUri);
String emailBodyTemplate;
String emailSubject;
emailBodyTemplate = EmailTemplates.getEmailTemplateByName(EmailConstants.TEMPLATE_DELIVERABLE_ACCEPTED_REJECTED_WITH_COMMENTS);
emailSubject = String.format(EmailConstants.ACCEPT_REJECT_WITH_COMMENTS_SUBJECT, ChecklistName, targetPhase);
emailBodyTemplate = EmailTemplates.replaceSharedVariable(emailBodyTemplate, sharedInfo);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_TARGET_PHASE, targetPhase);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_REVIEWER, reviewer);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_NAME, ChecklistName);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_COMMENT, comment);
emailBodyTemplate = EmailTemplates.replaceVariable(emailBodyTemplate, EmailConstants.VAR_CHECKLIST_ITEM_URL, ChecklistLink);
try {
emailService.sendEmail(submitter, EmailConstants.EMAIL_SENDER, emailSubject, emailBodyTemplate);
} catch (RuntimeException e) {
Checklist.addError(messages.get(E_ACCEPT_REJECT_SEND_EMAIL));
}
}
//Method to be tested
public void method(Checklist checklist){
/*Some Code*/
sendEmail(checklist); /* want to ignore this, as throwing NullPointerException*/
/*Some Code*/
}}
I would suggest to change the scope of the private method to package or protected. You can then override it in a test class that extends your class.
I would not try to mock all the services used in the sendMail method, because your test will then depend on everything within the sendMail method even it doesn't need it. As a result your test must be changed whenever the internals of the sendMail method changes. This would be bad, because your test doesn't need the sendMail method - that's why you want to mock it.
Your test will also get very complex with all the mocks that are only necessary to make the sendMail method work.
A much better approach would be to extract the sendMail method into an interface and create an implementation with the content of the current sendMail method.
public interface ChecklistNotifier {
public void sendNotification(Checklist checklist);
}
public class EmailChecklistNotifier implements ChecklistNotifier {
public void sendNotification(Checklist checklist){
EmailService emailService = new EmailService();
BaseDTO esDO = newFolderService.getFolderByUri(ServicesUtils.getDecodedCaseNodeUriFromSelfLink(Checklist.getEs_uri()));
// ...
}
}
Your client class can then use a ChecklistNotifier.
public class ClientClass {
private ChecklistNotifier notifier;
public ClientClass(ChecklistNotifier notifier){
this.notifier = notifier;
}
public void method(Checklist checklist){
/*Some Code*/
notifier.sendnotification(checklist);
/*Some Code*/
}}
}
Now you can easily create a ClientClass instance in your test and pass it a ChecklistNotifier mock or just a simple implementation.
This approach also honors the SOLID principles, because you have
a single responsibility for both the ClientClass and the EmailChecklistNotifier
made it open-close - you can replace it with a mock or other implementions, maybe SMS notification
a segregated interface - ChecklistNotification
inversed the dependency and therfore decoupled the ClientClass from the mail sending implementation dependencies.
You are correct. Powermock does not yet support JUnit 5 and there is an open issue in their official github repository here.
There doesnt seem to be any easy way to mock private methods using Junit5 runner , unless of course you decide to use a custom classloader and do bytecode manipulation.
However, instead of mocking the whole method, I would suggest you to mock the dependency which is used to send the email (unless that dependency uses some final method).
And if you can't even do that, then best way is to use Junit4 instead of Junit5.
I have implemented the custom filter factories for Cloud Gateway. However, I couldn't figure out the way to write unit testcases.
While exploring the default Filter Factories test cases, I found that majority of factories test classes extends BaseWebClientTests and other classes which are in test package.
My question is that shall I copy paste those intermediate test classes to my local test package? What's community recommendation here?
Here is my result, for your information
class CustomGatewayFilterFactoryTest {
#Autowired
private CustomGatewayFilterFactory factory;
private ServerWebExchange exchange;
private GatewayFilterChain filterChain = mock(GatewayFilterChain.class);
private ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);
#BeforeEach
void setup() {
when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
}
#Test
void customTest() {
MockServerHttpRequest request = MockServerHttpRequest.get(DUMMY_URL).build();
exchange = MockServerWebExchange.from(request);
GatewayFilter filter = factory.apply(YOUR_FACTORY_CONFIG);
filter.filter(exchange, filterChain);
// filter.filter(exchange, filterChain).block(); if you have any reactive methods
ServerHttpRequest actualRequest = captor.getValue().getRequest();
// Now you can assert anything in the actualRequest
assertEquals(request, actualRequest);
}
}
I'm a bit sceptic of this code, since it kind of violates YAGNI. I need some public static fields for my service class, but usually it's a bad design when you have those in your service bean, so I created an interface for that service, but I'm not sure it's the right choice. Should I have those fields in service class?
public interface IYouTubeServiceBuild {
/**
* Define a global instance of the HTTP transport.
*/
public static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
/**
* Define a global instance of the JSON factory.
*/
public static final JsonFactory JSON_FACTORY = new JacksonFactory();
/**
* Define a global variable that identifies the name of a file that
* contains the developer's API key.
*/
public static final String PROPERTIES_FILENAME = "youtube.properties";
}
#Service
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class YouTubeServiceBuild implements IYouTubeServiceBuild {
#Getter
private Properties properties;
/**
* Define a global instance of a Youtube object, which will be used
* to make YouTube Data API requests.
*/
#Getter
private YouTube youtube;
#PostConstruct
public void init() {
properties = new Properties();
youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, request -> {
}).setApplicationName("youtube-search-demo").build();
//etc...
}
}
}
Above service class is then used in other service as follows:
#Service
public class YouTubeApiService {
#Autowired
private YouTubeServiceBuild serviceBuild;
public List<SearchResult> searchYouTube(String searchQuery) {
List<SearchResult> searchResults =
executeSearch(searchQuery, serviceBuild.getProperties(), serviceBuild.getYoutube());
//etc...
}
If the question is whether its possible to set public static fields in Spring managed bean - then yes, its possible, although I completely agree with you that its a bad design.
Leaving alone spring, putting constants in the interface so that the implementations of the interface will be able to access them is considered a code smell because all the implementations (assuming there are many) now can behave differently because some constant changes, or even worse - won't compile anymore if the constant is removed (what if not all the implementations are in the project).
Much better approach is creating a class of constants:
public class YouTubeConstants {
public static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
public static final JsonFactory JSON_FACTORY = new JacksonFactory();
...
}
In the class that needs an access to these constants you can use YouTubeConstants.HTTP_TRANSPORT (or even shorted with static imports).
Now as for the presented design, let me propose an alternative:
The class YouTubeApiService basically needs an access to YouTube object only, it needs it to perform queries. No need for YouTubeServiceBuild intermediate object, no need for connection properties), IMHO it only complicates the code.
You can do something like this:
#Service
public class YouTubeApiService {
#Autowired
private YouTube youtube;
public List<SearchResult> searchYouTube(String searchQuery) {
List<SearchResult> searchResults =
executeSearch(searchQuery,youtube);
//etc...
}
}
Looks much better, doesn't it?
Now in order to create a youtube object, you need some code that goes a little bit beyond the regular "new". You can use a Configuration for this:
import static YoutubeConstants.*;
#Configuration
public class YouTubeConfig {
#Bean
public YouTube youtube() {
return new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, request -> {
}).setApplicationName("youtube-search-demo").build();
}
}
In this implementation there is no need in a YouTubeBuilder at all
One more things to consider:
It seems that you're trying to load some properties by yourself, notice, spring can to it alone.
I haven't seen any real usage of these properties in the code presented in the question, so I can't provide the working example, but please make sure you understand how to load properties in spring, you might inject the already-loaded properties into the youtube method in the configuration.
I'm working on a component that creates HTTP requests dynamically, and I'd like to be able to mock those requests for unit testing.
Currently the implementation looks something like this:
class ModelClass {
public void populate() {
HTTPRequest request = new HTTPRequest();
//configure request...
request.send();
}
}
Is there a way to use Guice to instantiate request so I can replace it with an instance of a mock class for testing? The nearest I can figure out would be to add an injector as instance variable of ModelClass:
class ModelClass {
private final Injector injector;
ModelClass(Injector injector){
this.injector = injector;
}
public void populate() {
HTTPRequest request = injector.getInstance(HTTPRequest.class);
//configure request...
request.send();
}
}
But that's basically like using a factory, which misses the point of Guice entirely.
You can inject a provider which provides 'HTTPRequest' instances in your code.
class ModelClass {
#Inject
Provider<HTTPRequest> httpRequestProvider;
public void populate() {
HTTPRequest request = httpRequestProvider.get();
}
}
Then, in your test code, you can mock the 'httpRequestProvider' to return mock 'HTTPRequest' instances.
Provider<HTTPRequest> mockHttpRequestProvider = mock(Provider.class);
when(mockHttpReqestProvider.get()).thenReturn(yourMockHTTPRequestObject);
// Set this mock provider to the ModelClass instance. (You may have to use reflection)
Info on injecting providers: https://github.com/google/guice/wiki/InjectingProviders
I'm trying to set up unit testing. I'm using Struts2 and Liferay 6.1.
I'm getting the below error
java.lang.NullPointerException
at com.liferay.portal.util.PortalUtil.getCompany(PortalUtil.java:305)
at com.mycomp.portlet.action.BasePortletAction.setupSiteAgent(BasePortletAction.java:1169)
This is because PortalUtil.getPortal() returns null. Is there a way I could somehow create a mock portal? There is no MockPortal class. I have found something called MockPortalContext but I'm not sure how to make use of it.
This is my code so far
BaseTestCase.java
public abstract class BaseTestCase extends TestCase {
private Dispatcher dispatcher;
protected ActionProxy proxy;
private static MockServletContext servletContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
public BaseTestCase(String name) {
super(name);
}
#SuppressWarnings("unchecked")
protected <T>T createAction(Class<T> theClass, String namespace, String actionName, String methodName, HashMap<String, Object> actionContextMap, HashMap<String, Object> parameterMap) throws Exception {
proxy = dispatcher.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, actionName, methodName, new HashMap<String, Object>(), true, true);
for (String key : actionContextMap.keySet()) {
proxy.getInvocation().getInvocationContext().put(key, actionContextMap.get(key));
}
proxy.getInvocation().getInvocationContext().setParameters(parameterMap);
proxy.setExecuteResult(true);
ServletActionContext.setContext(proxy.getInvocation().getInvocationContext());
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
ServletActionContext.setRequest(request);
ServletActionContext.setResponse(response);
ServletActionContext.setServletContext(servletContext);
return (T) proxy.getAction();
}
protected void setUp() throws Exception {
final String[] config = new String[]{"struts.xml", "mockApplicationContext.xml"};
servletContext = new MockServletContext();
final XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setServletContext(servletContext);
appContext.setConfigLocations(config);
appContext.refresh();
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appContext);
HashMap<String, String> params = new HashMap<String, String>();
params.put("actionPackages", "com.mycomp.portlet.action");
dispatcher = new Dispatcher(servletContext, params);
dispatcher.init();
Dispatcher.setInstance(dispatcher);
}
}
ActionTest.java
public class ActionTest extends BaseTestCase {
private Map<String, Object> contextMap;
private Map<String, Object> parameterMap;
private MockPortletRequest portletRequest;
private final String REQUEST_LOCALE = "request_locale";
public ActionTest(String name) {
super(name);
}
public void testShowDetail() throws Exception {
init();
parameterMap = new HashMap<String, Object>();
parameterMap.put(REQUEST_LOCALE, "en_GB");
#SuppressWarnings("unused")
PortletAction lspa = createAction(PortletAction.class,
"/view",
"myAction",
"myAction",
(HashMap<String, Object>)contextMap,
(HashMap<String, Object>)parameterMap);
String result = proxy.execute();
System.out.println(result);
}
private void init() {
portletRequest = new MockPortletRequest();
contextMap = new HashMap<String, Object>();
contextMap.put(PortletActionConstants.REQUEST, portletRequest);
}
}
The Spring documentation says creating a MockPortletRequst() with the no-arg constructor creates it with a default MockPortletContext and MockPortalContext so I don't know why it's null.
Use Powermock or jMockit to mock the static method call PortalUtil.getPortal()
Technically the answer has already been given by John B. I'd like to add a philosophical angle.
IMHO mocking a complex environment like a portal doesn't buy a lot, especially when we speak about unit testing. You'll gain more insight into your code by minimizing contact with any complex API and environment (not just a portal), rather decouple from that APIs.
One solution is to have very simple wiring in portlet classes (and code-review this) while extracting testable code into its own - fully tested - classes that don't call out to the external API, rather get their context passed in.
This would leave you with some very simple code that's not unit-tested, but in addition to the code-review you can (and should) add some integration/smoke tests that actually work in the full environment.
Sometimes a simple solution will be to quickly mock portal classes (or other external classes), but I don't see this as the preferred solution. As soon as you start writing significant setup code to prepare the environment, you've gained nothing when your test runs. And if it fails, you'll have to check it in the real environment anyways to see if your setup was accurate.
Sorry if this is bad news - IMHO it's inherent when you have any given API that has not been built with being replaceable in unit tests. And with my unwillingness to routinely have large setup routines in unit tests. I'd not call them unit tests, if this happens frequently - rather break down the (too complex) unit into more smaller ones. Or accept code-review for simple wiring (adaptation) code between two different APIs.