繁体   English   中英

如何使用Mockito对依赖equals()的抽象类方法进行单元测试?

[英]How to unit test an abstract class method that depends on equals() with Mockito?

我需要在Java中测试一个抽象类,并且我有一个依赖于equals()来提供结果的方法。

public abstract class BaseClass<T extends BaseClass<T>> {

    public boolean isCanonical() {
        return toCanonical() == this;
    }

    public T toCanonical() {
        T asCanonical = asCanonical();
        if (equals(asCanonical)) {
            //noinspection unchecked
            return (T) this;
        }
        return asCanonical;
    }

    protected abstract T asCanonical();
}

正如您所看到的, toCanonical()方法将自身与asCanonical构建进行比较,调用抽象方法asCanonical()

为了测试这个,我需要创建一个抽象类的空实现,并使mockito为两个实现的方法调用实际方法,并为asCanonical()返回我自己的类。

BaseClass aCanonicalClass;
BaseClass aNonCanonicalClass;

@Mock
BaseClass sut;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    // these are the methods under test, use the real ones
    Mockito.when(sut.isCanonical())
            .thenCallRealMethod();
    Mockito.when(sut.toCanonical())
            .thenCallRealMethod();
}

通常,如果这不是equals()方法,我会这样做:

@Test
public void test_isCanonical_bad() {
    // test true
    Mockito.when(sut.equals(aCanonicalClass))
            .thenReturn(true);
    Mockito.when(sut.equals(aNonCanonicalClass))
            .thenReturn(false);
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertTrue(sut.isCanonical());

    // test false
    Mockito.when(sut.equals(aNonCanonicalClass))
            .thenReturn(true);
    Mockito.when(sut.equals(aCanonicalClass))
            .thenReturn(false);
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertFalse(sut.isCanonical());
}

哪个给出了这个错误:

 org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'. For example: when(mock.getArticles()).thenReturn(articles); Also, this error might show up because: 1. you stub either of: final/private/equals()/hashCode() methods. Those methods *cannot* be stubbed/verified. Mocking methods declared on non-public parent classes is not supported. 2. inside when() you don't call method on mock but on some other object. 

Mockito的一个限制是它不允许模拟equals()hashcode()方法。

作为一种解决办法时,我想测试的条件我只是通过自己equals() = true当我想要测试的条件和其他随机对象equals() = false

@Test
public void test_isCanonical() {
    // test true
    Mockito.when(sut.asCanonical())
            .thenReturn(sut);
    Assert.assertTrue(sut.isCanonical());

    // test false
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertFalse(sut.isCanonical());
}

但即使我将实现更改为此,此测试也会通过:

    public T toCanonical() {
        T asCanonical = asCanonical();
        if (this == asCanonical) {
            //noinspection unchecked
            return (T) this;
        }
        return asCanonical;
    }

甚至这个:

    public T toCanonical() {
        return asCanonical();
    }

这是错的! 应该用equals进行比较。 通过引用这样做是不一样的。

当我到达toCanonical()方法时,事情变得无法测试:

@Test
public void test_toCanonical_bad() {
    // test canonical
    Mockito.when(sut.equals(aCanonicalClass))
            .thenReturn(true);
    Mockito.when(sut.equals(aNonCanonicalClass))
            .thenReturn(false);
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertSame(sut, sut.toCanonical());

    // test non-canonical
    Mockito.when(sut.equals(aNonCanonicalClass))
            .thenReturn(true);
    Mockito.when(sut.equals(aCanonicalClass))
            .thenReturn(false);
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertNotEquals(sut, sut.toCanonical());
    Assert.assertSame(aCanonicalClass, sut.toCanonical());
}

这当然不起作用,并且应用相同的解决方法并没有多大意义,因为我只测试函数的返回值是asCanonical()的返回值:

@Test
public void test_toCanonical() {
    // test canonical
    Mockito.when(sut.asCanonical())
            .thenReturn(sut);
    Assert.assertSame(sut, sut.toCanonical());

    // test non-canonical
    Mockito.when(sut.asCanonical())
            .thenReturn(aCanonicalClass);
    Assert.assertNotEquals(sut, sut.toCanonical());
    Assert.assertSame(aCanonicalClass, sut.toCanonical());
}

这两个测试都没有意义,因为我无法模拟equals()的结果。

有没有办法测试至少equals()是用我给它的对象调用的? (间谍)

有没有其他方法来测试这个?

编辑 :这是我正在使用的实际代码的简化。

我修改了示例代码以至少显示toCanonical()asCanonical()方法应该返回同一个类的实例(而不仅仅是任何类)

我想在这里测试基类。 只有具体的实现知道如何构建一个实际的规范类( asCanonical() )或检查这个类是否等于规范类( equals() )。 请注意, toCanonical()返回toCanonical()如果它等于自身的规范版本。 equals != same

实际的实现是不可变的类。 因为它们很多,而且这个代码对于我把它放在基类中的每个人都是一样的。

你为什么要测试一个抽象类? 如果你真的想测试toCanonical方法(这不是final ,所以你可以稍后覆盖它),你可以实例化一个临时具体类并测试它。 这比使用Mockito和其他东西更容易。

即:

@Test
public void myAwesomeTest() {
    // Arrange
    BaseClass test = new BaseClass<String>() {
       // provide concrete implementation
       protected String asCanonical() {
             return "Foo";
       }
    };

    // Act
    String result = test.toCanonical("Bar");

    // Assert

}

一旦简单的情况工作,您可以将baseclass的创建提取到工厂方法,即MakeBaseClass(String valueToReturnForAsCanonical并编写更多测试。

如果您在某些未经测试的代码区域中使用Spy是很方便的,并且您无法在代码中存根零件或创建一些接缝。 在这种情况下,我认为你可以自己编写样板,而不是依靠Mockito的魔法。

祝好运!

暂无
暂无

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

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