有没有办法模拟门户网站?

时间:2015-02-12 18:20:46

标签: java unit-testing junit struts2 liferay

我正在尝试设置单元测试。我正在使用Struts2和Liferay 6.1。

我收到以下错误

java.lang.NullPointerException
at com.liferay.portal.util.PortalUtil.getCompany(PortalUtil.java:305)
at com.mycomp.portlet.action.BasePortletAction.setupSiteAgent(BasePortletAction.java:1169)

这是因为PortalUtil.getPortal()返回null。有没有办法我可以以某种方式创建一个模拟门户网站?没有MockPortal类。我发现了一些名为MockPortalContext的东西,但我不确定如何使用它。

这是我目前的代码

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

}

Spring documentation说使用no-arg构造函数创建MockPortletRequst()会使用默认MockPortletContextMockPortalContext创建它,所以我不知道为什么它为null。< / p>

2 个答案:

答案 0 :(得分:1)

使用Powermock或jMockit模拟静态方法调用PortalUtil.getPortal()

答案 1 :(得分:0)

从技术上讲,John B.已经给出了答案。我想补充一个哲学角度。

嘲笑像门户网站这样的复杂环境的恕我直言并不买很多,尤其是当我们谈论单元测试时。通过最大限度地减少与任何复杂API和环境(不仅仅是门户网站)的联系,您可以更深入地了解您的代码,而不是与API分离。

一种解决方案是在portlet类中进行非常简单的连接(并对代码进行查看),同时将可测试代码提取到自己的 - 经过充分测试的类中,这些类不会调用外部API,而是将其上下文传递给

这将为您提供一些非经过单元测试的非常简单的代码,但除了代码审查之外,您还可以(并且应该)添加一些实际在整个环境中工作的集成/冒烟测试。

有时一个简单的解决方案是快速模拟门户类(或其他外部类),但我不认为这是首选解决方案。一旦您开始编写重要的设置代码来准备环境,您的测试运行时就没有任何收获。如果它失败了,你将不得不在真实环境中检查它,看看你的设置是否准确。

对不起,如果这是个坏消息 - 恕我直言,当你有任何给定的API没有在单元测试中可更换时,它是固有的。而且我不愿意在单元测试中经常进行大型设置程序。如果经常发生这种情况,我不会称它们为单元测试 - 而是将(过于复杂的)单元分解为更小的单元。或者接受两个不同API之间的简单布线(适配)代码的代码审查。