- Old approach: Utilization of Mockito and JSF / Servlet mocks from "dead" Apache Shale, Spring, ebtam FacesContext.
- Disadvantages: Limitations, no mocking of static, final, private classes / methods. Mix of different mocks causes an unconsistent environment. Hacks for the production code.
- New approach: Utilization of PowerMock and MyFaces Test frameworks.
- Advantages: Mocking of static, final, private classes / methods and more is possible. Unified mock objects and consistent weaving for all fundamental JSF / Servlet pendants.
- JavaServer Faces
- Servlet
package com.xyz.webapp.basemock;
import org.apache.myfaces.test.base.junit4.AbstractJsfTestCase;
import org.apache.myfaces.test.mock.*;
import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
public class JsfServletMock extends AbstractJsfTestCase
{
public MockApplication20 mockApplication;
public MockExternalContext20 mockExternalContext;
public MockFacesContext20 mockFacesContext;
public MockLifecycle mockLifecycle;
public MockServletContext mockServletContext;
public MockServletConfig mockServletConfig;
public MockHttpSession mockHttpSession;
public MockHttpServletRequest mockHttpServletRequest;
public MockHttpServletResponse mockHttpServletResponse;
public JsfServletMock() {
try {
super.setUp();
} catch (Exception e) {
throw new IllegalStateException("JSF / Servlet mocks could not be initialized");
}
// JSF API
mockApplication = (MockApplication20) application;
mockExternalContext = (MockExternalContext20) externalContext;
mockFacesContext = (MockFacesContext20) facesContext;
mockLifecycle = lifecycle;
// Servlet API
mockServletContext = servletContext;
mockServletConfig = config;
mockHttpSession = session;
mockHttpServletRequest = request;
mockHttpServletResponse = response;
}
}
An object of this class can be used in tests per delegation pattern. Define the PowerMockRunner with @RunWith at the beginning. Define classes which static, final, private methods will be mocked / tested with @PrepareForTest. Take an example.import com.xyz.webapp.basemock.JsfServletMock;
import com.xyz.webapp.util.FacesUtils;
import com.xyz.webapp.util.Observer;
import com.xyz.webapp.util.ObserverUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest({FacesUtils.class, ObserverUtil.class})
public class DataExportWorkflowPageTest
{
private JsfServletMock jsfServletMock;
@Mock
private StatusProfileFinder statusProfileFinder;
@Mock
private Collection<Observer> observers;
private DataExportWorkflowPage dewp = new DataExportWorkflowPage();
@Before
public void before() {
jsfServletMock = new JsfServletMock();
dewp.setObservers(observers);
dewp.setStatusProfileFinder(statusProfileFinder);
}
@Test
public void initialize() {
mockStatic(FacesUtils.class);
DataExportHistoryForm dehf = mock(DataExportHistoryForm.class);
DataExportContentForm decf = mock(DataExportContentForm.class);
WorkflowData workflowData = mock(WorkflowData.class);
when(FacesUtils.accessManagedBean("dataExportHistoryForm")).thenReturn(dehf);
when(FacesUtils.accessManagedBean("dataExportContentForm")).thenReturn(decf);
List<StatusProfile> spList = new ArrayList<StatusProfile>();
StatusProfile sp1 = new StatusProfile(GenericObjectType.DOCUMENT);
sp1.setBusinessKey("document");
spList.add(sp1);
StatusProfile sp2 = new StatusProfile(GenericObjectType.ACTIVITY);
sp2.setBusinessKey("activity");
spList.add(sp2);
StatusProfile sp3 = new StatusProfile(GenericObjectType.EXPENSE_DOCUMENT_ITEM);
sp3.setBusinessKey("expense");
spList.add(sp3);
jsfServletMock.mockHttpSession.setAttribute(Constants.SESSION_CLIENT, new Client());
when(statusProfileFinder.getAllLimitBy(any(Client.class), eq(Constants.MAX_RESULTS_DEFAULT))).thenReturn(spList);
when(FacesUtils.accessManagedBean("workflowData")).thenReturn(workflowData);
dewp.initialize();
verify(observers).add(dehf);
verify(observers).add(decf);
verifyNoMoreInteractions(observers);
assertEquals(GenericObjectType.DOCUMENT.getId(), dewp.getStatusProfile());
assertEquals(workflowData, dewp.getWorkflowData());
}
...
}
After JsfServletMock instantiation in a method annotated with @Before we have a full access to mock objects. E.g. MockHttpSession can be accessed by jsfServletMock.mockHttpSession. In the example I want to mock the static method FacesUtils.accessManagedBean(String) in the static class. We have to write firstmockStatic(FacesUtils.class);to achieve that. And then quite normally
when(FacesUtils.accessManagedBean(...)).thenReturn(...);In another small example I use the call
verifyStatic();to verify if the subsequent static call got really called. In the example below we verify if
ObserverUtil.notifyAll(depd);was called within
depd.setProcedureStep2(null);
@RunWith(PowerMockRunner.class)
@PrepareForTest(ObserverUtil.class)
public class DataExportProcedureDataTest
{
private DataExportProcedureData depd;
@Mock
private Collection<Observer> observers;
@Before
public void before() {
depd = new DataExportProcedureData();
depd.setObservers(observers);
}
@Test
public void setProcedureStep2() {
mockStatic(ObserverUtil.class);
depd.setProcedureStep2(null);
verifyStatic();
ObserverUtil.notifyAll(depd);
}
...
}
You see we can do both - mock static methods and verify behaviors.Bring power to JSF tests! No need for workarounds any more! Keep your production code clean from the test driven stuff!
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.