简体   繁体   English

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

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

I need to test an abstract class in Java, and I have a method which depends on equals() to provide a result. 我需要在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();
}

As you can see the toCanonical() method compare itself with asCanonical build calling the abstract method asCanonical() . 正如您所看到的, toCanonical()方法将自身与asCanonical构建进行比较,调用抽象方法asCanonical()

To test this I need to create an empty implementation of my abstract class and make mockito calls real methods for the two implemented method and return my own classes for the 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();
}

Normally if this wasn't the equals() method I would just do this: 通常,如果这不是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());
}

Which give this error: 哪个给出了这个错误:

 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. 

One of Mockito limitations is that it doesn't allow to mock the equals() and hashcode() methods. Mockito的一个限制是它不允许模拟equals()hashcode()方法。

As a workaround I just pass itself when I want to test the condition equals() = true and another random object when I want to test the condition equals() = false . 作为一种解决办法时,我想测试的条件我只是通过自己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());
}

But this test pass even if I change the implementation to this: 但即使我将实现更改为此,此测试也会通过:

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

or even this: 甚至这个:

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

which is wrong! 这是错的! Comparison should be done with equals. 应该用equals进行比较。 Doing so by reference is not the same thing. 通过引用这样做是不一样的。

Things gets impossible to test when I get to the toCanonical() method: 当我到达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());
}

This of course doesn't work, and doesn't really makes sense to apply the same workaround because I would just test that the return value of the function is the one of asCanonical() : 这当然不起作用,并且应用相同的解决方法并没有多大意义,因为我只测试函数的返回值是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());
}

Both test are pointless because of the fact I can't mock the result of equals() . 这两个测试都没有意义,因为我无法模拟equals()的结果。

Is there a way to test at least equals() is being called with the object I gave it? 有没有办法测试至少equals()是用我给它的对象调用的? (spy it) (间谍)

Is there any other way to test this? 有没有其他方法来测试这个?

EDIT : this is a semplification of the actual code I'm working with. 编辑 :这是我正在使用的实际代码的简化。

I modified the example code to at least show the toCanonical() and asCanonical() methods are supposed to return instances of the same class (and not just any class) 我修改了示例代码以至少显示toCanonical()asCanonical()方法应该返回同一个类的实例(而不仅仅是任何类)

I want to test the base class here. 我想在这里测试基类。 Only the concrete implementation know how to build an actual canonical class ( asCanonical() ) or check if this class is equal to a canonical class ( equals() ). 只有具体的实现知道如何构建一个实际的规范类( asCanonical() )或检查这个类是否等于规范类( equals() )。 And please note that toCanonical() return ITSELF if it is equals to the canonical version of itself. 请注意, toCanonical()返回toCanonical()如果它等于自身的规范版本。 Again equals != same . equals != same

The actual implementations are immutable classes. 实际的实现是不可变的类。 And since they are many and this code is the same for everyone i put it in a base class. 因为它们很多,而且这个代码对于我把它放在基类中的每个人都是一样的。

Why would you test an abstract class? 你为什么要测试一个抽象类? If you really want to test the toCanonical method (which is not final , so you can override it later) you can instantiate a temp concrete class and test it. 如果你真的想测试toCanonical方法(这不是final ,所以你可以稍后覆盖它),你可以实例化一个临时具体类并测试它。 That is way easier then using Mockito and other stuff. 这比使用Mockito和其他东西更容易。

Ie: 即:

@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

}

Once the simple case works, you can extract the creation of baseclass into a factory method ie MakeBaseClass(String valueToReturnForAsCanonical and write more tests. 一旦简单的情况工作,您可以将baseclass的创建提取到工厂方法,即MakeBaseClass(String valueToReturnForAsCanonical并编写更多测试。

Using Spy's are handy if you are in some regions of untested code and you have no way to stub out parts or create some seams in the code. 如果您在某些未经测试的代码区域中使用Spy是很方便的,并且您无法在代码中存根零件或创建一些接缝。 In this case I think you can suffice with writing the boilerplate yourself instead of relying on some magic by Mockito. 在这种情况下,我认为你可以自己编写样板,而不是依靠Mockito的魔法。

Good luck! 祝好运!

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

相关问题 Mockito。 如何在被测类中模拟抽象方法调用? - Mockito. How to mock abstract method call in class under test? 如何对作为 Mockito 中的构建器类的方法参数进行单元测试? - How to Unit Test A Method Argument which is a Builder Class in Mockito? Mockito:如何单元测试void方法Java Mockito? - Mockito: How do unit test void method java mockito? 如何使用Java中的Junit Mockito处理依赖抽象方法的测试类 - How to deal with test class who is having dependency on abstract method using junit mockito in java 如何使用Mockito在Service类中测试void方法? - How test a void method into Service class, with Mockito? jUnit,Mockito。 如何确保抽象类中的方法(模板方法)调用抽象钩子方法? - jUnit, Mockito. How to ensure a method in abstract class (a templae method) invokes the abstract hook method? 类中的Mockito和final equals()方法-是否可模拟? - Mockito and final equals() method in class - is it mockable? 如何使用基于Spring的单元测试模拟返回Servlet上下文的抽象类中可用的静态方法 - How to mock static method available in abstract class which returns servlet context using spring based unit test 如何使用Mockito模拟静态方法以进行单元测试 - How to mock the static method with mockito to do unit test 如何使用Mockito对链式方法调用进行单元测试 - How to unit test chained method call(s) using Mockito
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM