10

以下是我对单元测试的要求:

  1. 我想对我的生产类进行单元测试
  2. 我想将测试代码和生产代码分开,这样我只能发布生产代码

这似乎是合理的要求。但是,当我需要使用诸如assertEquals对象之类的方法时,总是会出现问题,因为这些方法需要equals覆盖该方法。该equals方法必须在生产类中实现,但实际上仅用于测试。equals当良好的编码实践规定如果被覆盖,则hashCode也应该实现,从而导致更多未使用的生产代码使生产类混乱,这会变得更糟。

这是一个带有User模型的简单示例(IntelliJ autoimplemented equalsand hashCode

public class User
{
    public long id;
    public long companyId;
    public String name;
    public String email;
    public long version;

    @Override
    public boolean equals(Object o)
    {
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        if(companyId != user.companyId) return false;
        if(id != user.id) return false;
        if(version != user.version) return false;
        if(!email.equals(user.email)) return false;
        if(!name.equals(user.name)) return false;
        return true;
    }

    @Override
    public int hashCode()
    {
        int result = (int) (id ^ (id >>> 32));
        result = 31 * result + (int) (companyId ^ (companyId >>> 32));
        result = 31 * result + name.hashCode();
        result = 31 * result + email.hashCode();
        result = 31 * result + (int) (version ^ (version >>> 32));
        return result;
    }
}

可以看出,equals占用hashCode了大量空间并使课堂变得混乱。

该问题的一种解决方案可能是创建一个类 ,UserTester它可以有一个assertUserEquals可以用来代替例如的方法。JUnit 的assertEquals.

另一种解决方案可能是创建一个UserComparator. 但是,JUnit 似乎没有任何assertEquals需要Comparator.

在这一点上的最佳实践是什么?

4

3 回答 3

1

Uniutils有一个完美的反射等于方法,可用于单元测试。这样,您的生产代码就可以从所有这些测试内容中保持清晰。

public class User { 

    private long id; 
    private String first; 
    private String last; 

    public User(long id, String first, String last) { 
        this.id = id; 
        this.first = first; 
        this.last = last; 
    } 
}

稍后在测试中:

User user1 = new User(1, "John", "Doe"); 
User user2 = new User(1, "John", "Doe"); 
assertReflectionEquals(user1, user2);

如果您使用的是 Mockito,它有自己的方式来做同样的事情:

Mockito.verify(userDeleter).delete(Mockito.refEq(user));
于 2013-08-13T17:30:59.940 回答
0

不是最有效但一种可能的方法是使用反射比较字段。

public class Foo {

    int x;

    Foo(int in){
    x = in;
    }

    public static void main(String[] args) throws Exception{

        Foo o1 = new Foo(1),o2= new Foo(1);
        boolean allMatch = true;
        Class<?> c = Class.forName("Foo");
        Field[] fields =  c.getDeclaredFields();

        for(Field f: fields){
            allMatch &= f.get(o1)==f.get(o2);
        }
    }
}
于 2013-08-13T17:31:43.597 回答
0

我看到两个不同的东西:

  1. 有时不希望覆盖 hashcode&equals。它可以改变程序的行为并且会损害您的性能,请参阅Java Overriding hashCode() 方法有任何性能问题吗?

  2. 如果没有客户要求覆盖 hashcode&equals,就像它的值对象一样,你不会这样做。您应该提供完全符合客户标准的代码,而不是更多。您的测试应该处理默认对象中的原始实现。

于 2013-08-13T17:56:55.297 回答