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.
Related
Really short question: How can I mock response.getContentType() ?
(Using PowerMock + TestNG)
I'm not calling any new() methods.
I'm trying to mock class, that is result of method execution of some other class.
The class under test:
class ClassToBeMocked {
public String getJsonPage(String jsonUrl) throws IOException {
WebClient webClient = new WebClient(BrowserVersion.CHROME);
final Page page = webClient.getPage(jsonUrl);
final WebResponse response = page.getWebResponse();
final String cType = response.getContentType();
if (cType.equals("application/json") || cType.equals("application/hal+json")) {
return response.getContentAsString();
}
throw new IllegalArgumentException("Unexpected response type " + response.getContentType());
}
}
Test itself
#PrepareForTest( { WebResponse.class, ClassToBeMocked.class})
#PowerMockIgnore("javax.net.ssl.*")
public class UrlPullerTest extends PowerMockTestCase {
#Test
public void testGetPage() throws Exception {
WebResponse mockwebResposne = PowerMockito.mock(WebResponse.class);
PowerMockito.when(mockwebResposne.getContentType()).thenReturn("wrongType");
ClassToBeMocked classToBeMocked = new ClassToBeMocked();
classToBeMocked.getJsonPage("http://google.com");
}
}
You wouldn't. Your problem is that you created hard to test code, by putting that new WebClient call into your source code. That leads to direct coupling of implementations.
You should use dependency injection instead (for example to inject a factory that creates WebClient objects for you). Doing so, you can do all your work with power-less frameworks such as EasyMock or Mokito.
Hint: far too often, the usage of PowerMock is an indication, that your design could be improved. No idea what I am talking about? Then watch these videos. Each one worth each minute!
I'm working on a project where I need to call many different services and wanted to abstract out as much of the common logic as I can. The kicker is that I also want to return custom objects instead of something such as json. As I designed a way of doing this, I arrived at a paradigm that reminds me of a Strategy Pattern, but I don't quite think it fits. I'm wondering if there are any design flaws or dangers in how I've done this.
Basically I've created an abstract class (ServiceCall) that holds the common logic for calling a service (have an internet client and get a json response from the desired service). This is all done with the public method call(Map<String, String> params, Map<String, String> headers). The abstract class also defines two abstrach methods: createCustomDTO(Map<String, String> params, Map<String, String> headers) and parseResponseToObject(String json). I'll explain the purpose of these in just a second.
Each call to a different service will be created with a class that extends ServiceCall and creates an implementation of the abstract methods. createCustomDTO will create a custom object that contains all the information needed to call the service (url, and headers). parseResponseToObject will take a json response and turn it into the java object I want to use later in my code.
Here is a simple implementation of what I did
ServiceCall
public abstract class ServiceCall {
protected abstract Object parseResponseToObject(String json);
protected abstract CustomServiceDTO createDTO(Map<String, String> keys,
Map<String, String> headers);
public Object call(Map<String, String> params, Map<String, String> headers) {
// create and configure a CustomServiceDTO to call services with
CustomServiceDTO dto = createDTO(params, headers);
try {
// make the service request
String json = getServiceResponse(dto);
catch (Exception e) {
return new CustomServiceError(e);
}
// parse the response into the desired java object
Object response = parseResponseToObject(json);
return response;
}
private String getServiceResponse(CustomServiceDTO dto) {
// use common logic to call the service using the information provided
// by the dto
String dummyResponse = "{\"prop\":\"value\"}";
return dummyResponse;
}
}
SampleImplementation
public class AddressCall extends ServiceCall {
#Override
protected Object parseResponseToObject(String json) {
return new Address(json);
}
#Override
protected CustomServiceDTO createDTO(Map<String, String> keys,
Map<String, String> headers) {
CustomServiceDTO dto = new CustomServiceDTO();
dto.setUrl("www.getMyDummyAddress.com/" + keys.get(0) + "=" + keys.get(1));
dto.setHeaders(headers);
return dto;
}
}
The main drawback I see to this is that never directly calling createCustomDTO and parseResponseToObject is a little strange.
The main advantage for me is the ease of use and having my responses returned as java objects.
What I really want to know is are there any other concerns or drawbacks to this paradigm? It's very nice in the code, but I admit it seems a bit different from how java is normally used.
This is not different from how Java is normally used, this is called a Template Method design pattern.
It's pretty common, except for the use of Object, which is better replaced by a generic type.
It looks like EasyMock version 3.2 now supports using annotations to setup mock objects. I am new to EasyMock (and Java in general) and am trying to understand how to use this. Do these annotations do something new or just provide an alternative way to do things? The documentation says:
Since EasyMock 3.2, it is now possible to create mocks using annotations. This is a nice
and shorter way to create your mocks and inject them to the tested class.
Here is the example above, now using annotations: ...
Then there is a listing that shows use of the #TestSubject and #Mock annotations, but I don't understand how it works. It seems as if it magically sets the private field of the class under test to the mock object. In most of my cases, I just want to make mock objects that return pre-defined values for use in JUnit test cases (don't currently care about verifying which ones were called, how many times they were called, etc). For example, for some tests I want to create a fake HttpServletRequest object like this:
public class SomeTest {
// Construct mock object for typical HTTP request for the URL below
private static final String REQUEST_URL = "http://www.example.com/path/to/file?query=1&b=2#some-fragment";
private static final Map<String, String> requestHeaderMap;
static {
Map<String, String> requestHeaders = new LinkedHashMap<String, String>();
requestHeaders.put("host", "www.example.com");
// ... (add any other desired headers here) ...
requestHeaderMap = Collections.unmodifiableMap(requestHeaders);
}
private HttpServletRequest httpServletRequest;
// ...
#Before
public void setUp() throws Exception {
httpServletRequest = createNiceMock(HttpServletRequest.class);
expect(httpServletRequest.getRequestURI()).andReturn(REQUEST_URL).anyTimes();
expect(httpServletRequest.getHeaderNames()).andReturn(Collections.enumeration(requestHeaderMap.keySet())).anyTimes();
capturedString = new Capture<String>();
expect(httpServletRequest.getHeader(capture(capturedString))).andAnswer(new IAnswer<String>() {
public String answer() throws Throwable {
String headerName = capturedString.getValue().toLowerCase();
if (requestHeaderMap.containsKey(headerName))
return requestHeaderMap.get(headerName);
else
return "";
}
}).anyTimes();
replay(httpServletRequest);
// ...
}
#Test
public void someMethod_givenAnHttpServletRequest_shouldDoSomething() {
// ...
}
}
Could I change the above code to use annotations? If so, should I? Under what circumstances?
I thought perhaps putting the #Mock annotation above an instance variable declaration would automatically take care of the createNiceMock(...) part, but this does not seem to work, so I suspect that I am misunderstanding something.
Examining their source code, they are using reflection to inject anything with an #Mock into a field of the #TestSubject. Their javadoc for the method
public static void injectMocks(final Object obj)
in EasyMockSupport.java says:
Inject a mock to every fields annotated with {#link Mock} on the class passed in parameter. Then, inject these mocks to the fields of every class annotated with TestSubject.
The rules are
Static and final fields are ignored
If a mock can be assigned to a field, do it. The same mock an be assigned more than once
If no mock can be assigned to a field, skip it silently
If two mocks can be assigned to the same field, return an error
Fields are searched recursively on the superclasses
Note: If the parameter extends EasyMockSupport, the mocks will be created using it to allow replayAll/verifyAll to work afterwards
#param obj the object on which to inject mocks
#since 3.2
public static void injectMocks(final Object obj) {
...
}
For you to use the #Mock annotation, you would need a #TestSubject that has an HttpServletRequest field for EasyMock to set the #Mock on (via reflection). The annotations are provided to make it a little easier to wire up a test, it let's you skip the createMock, and then calling the settter yourself.
I am writing acceptance tests (testing the behavior) using cucumber-jvm, on an application with Struts 2 and Tomcat as my Servlet Container. At some point in my code, I need to fetch the user from the Struts 2 HttpSession, created by an HttpServletRequest.
Since I'm doing tests and not running Tomcat, I don't have an active session and I get a NullPointerException.
Here's the code I need to call:
public final static getActiveUser() {
return (User) getSession().getAttribute("ACTIVE_USER");
}
And the getSession method:
public final static HttpSession getSession() {
final HttpServletRequest request (HttpServletRequest)ActionContext.
getContext().get(StrutsStatics.HTTP_REQUEST);
return request.getSession();
}
In all honesty, I don't know much about Struts 2, so I need a little help. I've been looking at this cucumber-jvm with embedded tomcat example, but I'm struggling to understand.
I've also been looking at this Struts 2 Junit Tutorial. Sadly, it doesn't cover very well all the StrutsTestCase features and it's the simplest of use cases (all considered, a pretty useless tutorial).
So, how can I run my acceptance test as if the user was using the application?
UPDATE:
Thanks to Steven Benitez for the answer!
I had to do two things:
Mock the HttpServletRequest, as suggested,
Mock the HttpSession to get the attribute I wanted.
here's the code I've added to my cucumber-jvm tests:
public class StepDefs {
User user;
HttpServletRequest request;
HttpSession session;
#Before
public void prepareTests() {
// create a user
// mock the session using mockito
session = Mockito.mock(HttpSession.class);
Mockito.when(session.getAttribute("ACTIVE_USER").thenReturn(user);
// mock the HttpServletRequest
request = Mockito.mock(HttpServletRequest);
Mockito.when(request.getSession()).thenReturn(session);
// set the context
Map<String, Object> contextMap = new HashMap<String, Object>();
contextMap.put(StrutsStatics.HTTP_REQUEST, request);
ActionContext.setContext(new ActionContext(contextMap));
}
#After
public void destroyTests() {
user = null;
request = null;
session = null;
ActionContext.setContext(null);
}
}
An ActionContext is a per-request object that represents the context in which an action executes. The static methods getContext() and setContext(ActionContext context) are backed by a ThreadLocal. In this case, you can call this before your test:
Map<String, Object> contextMap = new HashMap<String, Object>();
contextMap.put(StrutsStatics.HTTP_REQUEST, yourMockHttpServletRequest);
ActionContext.setContext(new ActionContext(contextMap));
And then clean it up after with:
ActionContext.setContext(null);
This example will only provide what the method you are testing needs. If you need additional entries in the map based on code you didn't provide here, then just add them accordingly.
Hope that helps.
We are using spring oauth and there are several places where we need to use inheritance.
In the immediate case we are extending TokenEndpoint
public class MyTokenEndpoint extends TokenEndpoint {
//...
public ResponseEntity<OAuth2AccessToken> getAccessToken(
Principal principal,
MyParams myParams,
#RequestParam Map<String, String> allParams) {
// .. Stuff Happens
updateParamsWithStuff(allParams);
return super.getAccessToken(
principal, myParams.grantType(), allParams);
}
//...
}
Now what I want to test is if the map passed to super.getAcccessToken has been filled with Stuff. My simple way was to spy on the map passed in, but this relies on implemetation details and does not actually insure that stuff is in the map passed super.getAccessToken
We are using Mockito, I have seen comments that this will not work, and one that implies it may. Can this be done in any of the mocking frameworks?
See both answers on ( Can I mock a superclass's constructor with Mockito/Powermock?, the checked one says impossible, but given the discussion on the second answer I just had to try.)
After reading that I tried the following:
MyTokenEndpoint spyEndpoint = Mockito.spy(endpoint); //endpoint Set-up previously
Mockito.doAnswer(new Answer<ResponseEntity<OAuth2AccessToken>>() {
#Override
public ResponseEntity<OAuth2AccessToken>
answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Map<String, String> params = (Map<String, String>) args[2];
System.out.printf("%s\n", params.toString());
return new ResponseEntity<OAuth2AccessToken>(HttpStatus.ACCEPTED);
}
}).when(((TokenEndpoint) spyEndpoint))
.getAccessToken(any(Principal.class),
anyString(), (Map<String, String>) anyMap());
theResponse = spyEndpoint
.getAccessToken(principal,
myPrams,
currentMap);
But the code in answer never gets called.
Am I barking up the wrong tree?? Is this possible in any mocking framework?
Why do you need to mock? You are already extending the class--just override that method, examine the data passed in then forward the data to the parent.
Such a test can be easily written with JMockit:
#Test
public void mockCallToSuper(#Mocked final TokenEndpoint mockedBase)
{
final Principal principal = null; // or whatever
MyParams myParams = new MyParams();
Map<String, String> params = new HashMap<String, String>();
ResponseEntity<OAuth2AccessToken> accessToken =
new MyTokenEndpoint().getAccessToken(principal, myParams, params);
// asserts on "accessToken"
new Verifications() {{
Map<String, String> actualParams;
mockedBase.getAccessToken(
principal, (MyParams) any, actualParams = withCapture());
assertEquals("abc", actualParams.get("A"));
assertEquals("another item", actualParams.get("B2"));
}};
}