有没有办法模拟 Portal?

Is there a way to mock a Portal?

我正在尝试设置单元测试。我正在使用 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()returnsnull。有什么办法可以创建一个模拟门户吗?没有MockPortalclass。我找到了一个叫做 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 说使用无参数构造函数创建 MockPortletRequst() 会使用默认的 MockPortletContextMockPortalContext 创建它,所以我不知道为什么它为空.

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

技术上答案已经由 John B 给出。我想补充一个哲学角度。

恕我直言,模拟像门户这样的复杂环境并没有多大意义,尤其是当我们谈论 单元 测试时。通过尽量减少与 any 复杂 API 和环境(不仅仅是门户)的接触,而不是与 API 分离,您将更深入地了解您的代码。

一个解决方案是在 portlet 类 中进行非常简单的连接(并对此进行代码审查),同时将可测试代码提取到它自己的代码中 - 经过全面测试 - 类 不会调用外部 API,而是让他们的上下文传入。

这会给您留下一些未经单元测试的非常简单的代码,但除了代码审查之外,您还可以(并且应该)添加一些 integration/smoke 在完整环境中实际工作的测试。

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

抱歉,如果这是个坏消息 - 恕我直言,当您有任何给定的 API 尚未构建且无法在单元测试中替换时,这是固有的。而且我不愿意在 unit 测试中例行进行大型设置例程。如果这种情况经常发生,我不会称它们为单元测试——而是将(太复杂的)单元分解成更小的单元。或者接受两个不同 APIs.

之间的简单接线(适配)代码的代码审查