简体   繁体   English

正确的单元测试属性持有者,不等于实现

[英]Correct unit testing property holder with no equals implementation

I am having a debate with a colleague about a specific situation that I encountered, and would be great if someone could chime in with some view or theory grounding. 我正在与一位同事就我遇到的特定情况进行辩论,如果有人可以出于某种观点或理论基础而陷入困境,那将是非常棒的。

Let's say that we have model objects of type A. They are java beans, property holders, and have methods like getPrice, getQuantity, getName .. 假设我们有一个类型为A的模型对象。它们是Java Bean,属性持有者,并且具有诸如getPrice,getQuantity,getName等方法。

Let's also assume that for some legacy reason, the equals method returns true, on two different objects, even if they have different property values! 我们还假设由于某些传统原因,equals方法在两个不同的对象上返回true,即使它们具有不同的属性值!

I'll provide some code that exemplifies this problem. 我将提供一些代码来说明这个问题。 (Obviously not the same code, taken shortcuts) (显然不一样的代码,取了捷径)

 class A {
    private final double q;
    private final double p;

    public A(double q, double p) {
        this.q = q;
        this.p = p;
    }

    public double getQuantity()
    {
        return q;
    }
    public double getPrice()
    {
        return p;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        // not the actual method but a.equals(aWithDifferentValues) is True
        // this is the crux of the problem 
        return true;
    }

}

public abstract class Handler {
    protected Manager m;

    public Handler(Manager m) {
        this.m = m;
    }

    abstract public void handle(A a);
}

class HandlerA extends Handler {

    public HandlerA(Manager m) {
        super(m);
    }

    @Override
    public void handle(A a) {
        m.f(a, "abc");
    }
}

...

class HandlerC extends Handler {

    public HandlerC(Manager m) {
        super(m);
    }

    @Override
    public void handle(A a) {
        m.g(a, 1);
    }
}

class Manager {
    public void f(A a, String s) { }
    public void g(A a, double q) { }
}

We want to unit test HandlerA. 我们要对HandlerA进行单元测试。

So we might want to write a test like this: 因此,我们可能想编写如下测试:

public class TestMain {

    @Test
    public void givenA_fHappens() {

        Manager manager = mock(Manager.class);
        HandlerA handler = new HandlerA(manager);

        A givenA = new A(7, 9);

        handler.handle(givenA);
        verify(manager).f(givenA, "abc");
    }
}

The problem now arrises that because the equals returns true, for different A object with different properties making this modification in the code : 现在的问题是,由于equals返回true,因此对于具有不同属性的另一个A对象,需要在代码中进行此修改:

    @Override
    public void handle(A a) {
--        m.f(a, "abc");
++        m.f(new A(1, 1), "abc");
    }

will not be covered by the unit test 不会包含在单元测试中

I suggested that we use a matcher with the verify, (or asserts where there are argument captors) in fact there already is one called SamePropertyValueAs that could serve, but I was met with criticism that we don't want to assert that they have the same values, only that the code is called. 我建议我们在验证中使用匹配器(或断言存在参数捕获器的地方),实际上已经存在一个可以服务的名为SamePropertyValueAs的匹配器,但是我遭到批评,因为我们不想断言它们具有相同的值,只是代码被调用。

What do you think? 你怎么看? What are your opinions on this? 您对此有何看法?

The answer really lies on what you are aiming to test. 答案实际上取决于您要测试的内容。 Based on this the answers would vary. 基于此,答案将有所不同。

  1. Are you testing that the instance passed is the same in the sense reported by equals method? 你测试一个通过实例中所报告的意义相同 equals方法? If so, then verify(manager).f(givenA, "abc") is enough (provided you trust that object equality has been implemented for the given class). 如果是这样,那么verify(manager).f(givenA, "abc")就足够了(只要您相信已经为给定的类实现了对象相等性)。 In most cases, this is desirable because it makes more sense semantically and we don't want to worry about low level details such as object reference equality. 在大多数情况下,这是可取的,因为从语义上讲更有意义,并且我们不想担心诸如对象引用相等之类的底层细节。 In your example above, ideally the equals method should be fixed :) 在上面的示例中,理想情况下, equals方法应该是固定的:)

  2. Are you testing that the same object reference is passed to the method? 您是否正在测试是否将相同的对象引用传递给该方法? In some cases we might want to explicitly check that the object passed is indeed the same reference that was used internally and not substituted by a equivalent looking object. 在某些情况下,我们可能希望显式检查传递的对象确实是内部使用的相同引用,而不是被等效外观的对象替代。 This is rather rare, but if there is such a need, use an ArgumentCaptor and assert the reference equality between the sent and captured value. 这很少见,但是如果有此需要,请使用ArgumentCaptor并声明已发送值和捕获值之间的引用相等性。

Coming to the point where you mentioned we don't want to assert that they have the same values, only that the code is called . 到您提到的地步, 我们不想断言它们具有相同的值,只需要调用代码即可

Personally I don't agree that it is enough to assert that the method was called. 我个人并不认为断言该方法已被调用就足够了。 What if the value passed is different (like the one you mentioned). 如果传递的值不同(如您提到的值),该怎么办。 This would make the tests incomplete and make the code brittle. 这会使测试不完整并使代码变脆。 Such tests only make the coverage report look green but lack complete functional coverage. 这样的测试只会使覆盖率报告看起来绿色,但缺乏完整的功能覆盖率。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM