【问题标题】:jUnit:NPE on private member initializationjUnit:NPE 关于私有成员初始化
【发布时间】:2011-12-01 15:17:29
【问题描述】:

jUnit 测试,初始化我的bean:

ShowProducts sp = new ShowProducts();

在 ShowProducts.java 中的以下行获得 NullPointerException

    private Locale locale = FacesContext.getCurrentInstance().getViewRoot()
                .getLocale();

...
    public Locale getLocale() {
        return locale;
    }

    public String getLanguage() {
        return locale.getLanguage();
    }

    public void localize() {
        String localeParam = FacesContext.getCurrentInstance()
                .getExternalContext().getRequestParameterMap().get("lang");
        locale = new Locale(localeParam);
        FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
    }

如何在测试中正确初始化该字段?

编辑:

面孔配置:

<application>
    <locale-config>
        <default-locale>ru</default-locale>
        <supported-locale>ua</supported-locale>
    </locale-config>
    <resource-bundle>
        <base-name>msg</base-name>
        <var>m</var>
    </resource-bundle>
</application>

.xhtml:

    <h:form>
        <h:commandLink action="#{showProducts.localize}" includeViewParams="true"
                       rendered="#{showProducts.language=='ua'}">
            #{m.rus}.
            <f:param name="lang" value="ru"/>
        </h:commandLink>
        <h:commandLink action="#{showProducts.localize}" includeViewParams="true"
                       rendered="#{showProducts.language=='ru'}">
            #{m.ukr}.
            <f:param name="lang" value="ua"/>
        </h:commandLink>
    </h:form>

【问题讨论】:

    标签: java junit initialization field jmockit


    【解决方案1】:

    显然您的 JSF FacesContext 配置不正确(我对人脸了解不多,但我认为在 jUnit 测试中设置和运行它们非常复杂)。但是,有一些帮助正在进行中 - 使用模拟。

    在您的测试用例中,您希望确保: - ShowProducts 从人脸上下文/视图根中检索正确的语言环境 - 正确地做其他事情。

    我推荐你使用 jmockit。你的测试用例会变成这样:

     @Test
     public void testShowProducts(@Cascading final FacesContext facesContext) {
            final Locale locale = new Locale(...)
            new Expectations() {
               {
                  FacesContext.FacesContext.getCurrentInstance().getViewRoot().getLocale();
                  returns(locale);
               }
    
    
            };
           ShowProducts sp = new ShowProducts();
    
           ...  do your assertions other stuff there
     }
    

    此技术适用于多种上下文,大大简化了测试代码。

    【讨论】:

    • 当你只需要一个语言环境时传入一个完整的 FacesContext 似乎有点过头了。
    • 实际上这不是一个上下文,而是一个神奇的模型——所以没有矫枉过正。
    • 必须亲自尝试才能看到好处。感谢您推荐 jmockit。
    【解决方案2】:

    访问静态方法使编写测试变得困难。

    要么使用构造函数传递 Locale,要么使用 setter。

    最简单的更改是添加第二个以 Locale 作为参数的构造函数并将其用于单元测试。然后默认构造函数初始化 FacesContext 中的字段。

    为了实现更简洁的设计,您应该提取处理所有本地化的Localizer,以便将不需要FacesContextShowProducts 与需要的Localizer 分开。

    Localizer 类似于:

    public class Localizer {
        public void localize() {
            String localeParam = FacesContext.getCurrentInstance()
                    .getExternalContext().getRequestParameterMap().get("lang");
            locale = new Locale(localeParam);
            FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
        }
    }
    

    这与ShowProducts 无关。不确定您需要 getLanguge() 做什么。

    【讨论】:

    • CKuck,谢谢你的提示。所以看起来我的课程测试不好,最好重新设计它?
    • 糟糕的可测试性肯定是糟糕设计的标志 - 所以最好重新设计它(单例是邪恶的模式,应尽可能避免)。但如果你不能,你可以轻松地模拟面孔。 (见我的回答)
    • 康斯坦丁,谢谢你的帮助。那么你是否假设重构locale字段初始化?我不知道这是否可能,目前我需要调用getLanguage(),它依赖于语言环境,并且区域设置在 faces-config 中设置。请参阅我帖子中的 EDIT 部分。
    • localize() 中,您再次使用FacesContext 来获取ExternalContext。如果传入所有这些信息,这将是一个更简洁的设计。 -- 在我的答案中添加了改进设计的建议。
    • 看起来我也应该在 getLanguage() 中初始化语言环境,为了不获取 NPE,如果我将语言环境初始化作为私有成员删除,不是吗?
    猜你喜欢
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    • 1970-01-01
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 2016-09-06
    相关资源
    最近更新 更多