用内部类对单元测试类的正确方法

Mat*_*att 5 java junit unit-testing easymock

类A具有内部类B.类A具有通过getBs,addB和removeB方法提供的B类对象的私有列表.如何对removeB方法进行单元测试?

我希望创建两个相同的B类模拟,添加每个,然后删除其中一个两次(结果是删除它们).但是,我已经通过失败了解到不会在模拟对象上调用equals方法.

尝试将外部类与其内部类隔离以进行单元测试是愚蠢的(或不可能的)吗?


示例代码如下

要测试的类:

import java.util.ArrayList;
import java.util.List;

public class Example {
    private List<Inner> inners = new ArrayList<Inner>();

    public List<Inner> getInners() {
        return inners;
    }

    public void addInner(Inner i) {
        inners.add(i);
    }

    public void removeInner(Inner i) {
        inners.remove(i);
    }

    /**
     * equalityField represents all fields that are used for testing equality
     */
    public class Inner {
        private int equalityField;
        private int otherFields;

        public int getEqualityField() {
            return equalityField;
        }

        public Inner(int field) {
            this.equalityField = field;
        }

        @Override
        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (o.getClass() != this.getClass())
                return false;
            Inner other = (Inner) o;
            if (equalityField == other.getEqualityField())
                return true;
            return false;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

没有成功的测试用例:

import static org.junit.Assert.*;
import org.junit.Test;
import org.easymock.classextension.EasyMock;

public class ExampleTest {
    @Test
    public void testRemoveInner() {
        Example.Inner[] mockInner = new Example.Inner[2];
        mockInner[0] = EasyMock.createMock(Example.Inner.class);
        mockInner[1] = EasyMock.createMock(Example.Inner.class);        

        Example e = new Example();
        e.addInner(mockInner[0]);
        e.addInner(mockInner[1]);
        e.removeInner(mockInner[0]);
        e.removeInner(mockInner[0]);
        assertEquals(0, e.getInners().size());
    }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ark 6

你为什么要嘲笑你的内层阶级?如果遇到此问题,我将按原样使用类并测试外部类的行为是否按预期工作。您无需嘲笑内部类即可。

顺便说一句:当覆盖equals()方法时,建议也覆盖hashCode()方法。

  • 关于equals / hashcode:最好也覆盖hashcode,否则,它会破坏Java的一个或最重要的契约,使您的类无法在hashtable中使用,并在运行时生成一些奇怪的错误。 (4认同)
  • 几年太晚了,但我想我应该提供一些理由来解释为什么有人想要模拟:内部类,因为它负责一些不应在测试环境中发生的网络 IO。 (2认同)
  • 我认为这个答案没有意义。内部类和外部类之间会有一些交互,很明显这可能需要测试。为什么这是一个被接受的答案? (2认同)
  • 在 Android 中模拟内部类有一百万个理由。 (2认同)

Mat*_*ell 5

首先,你的问题的答案:是的,在单元测试时尝试分离内部和外部类通常是一个坏主意.通常,这是因为两者密切相关,例如Inner只在Outer的上下文中有意义,或者Outer有一个工厂方法返回一个接口的实现,一个Inner.如果两者没有真正链接,那么将它们分成两个文件.它使您的测试生活更轻松.

其次,(使用上面的代码作为示例),您实际上不需要模拟上面的代码.只需创建一些实例就可以了.看起来你有足够的合作.你总是可以这样做:

public void testRemoveInner() {
    Example.Inner[] inner = new Example.Inner(45);

    Example e = new Example();
    e.addInner(inner);
    e.addInner(inner);
    e.removeInner(inner);
    assertEquals(0, e.getInners().size());
}
Run Code Online (Sandbox Code Playgroud)

不需要嘲笑.

第三,尝试找出你实际测试的内容.在上面的代码中,您正在测试如果我向列表添加内容,那么我可以将其删除.顺便说一句,你说如果有多个对象是"相等的"; 上面的代码没有这样做,从Collection#remove()的定义:

从此集合中移除指定元素的单个实例(如果存在)(可选操作).更正式地,如果此集合包含一个或多个此类元素,则删除元素e(o == null?e == null:o.equals(e)).

这真的是你想要测试的吗?

第四,如果你实现equals,也要实现hashCode(参见在Java中覆盖equals和hashCode).