2017-02-15 95 views
3

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

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(); 
    } 

这是不对的!应该用平等来比较。这样做的参考是不一样的。

事情变得不可能的,当我到达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()被称为用我给的对象呢? (窥探它)

是否有任何其他的方式来测试这个?

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)

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()). And please note that toCanonical() return ITSELF if it is equals to the canonical version of itself. Again 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.

+0

我不明白为什么不能创建'BaseClass'的非空实现并将所有必要的测试逻辑放在那里?目前看起来你禁止自己在没有Mockito的情况下进行测试。 – user3707125

+0

这是我实际代码中的简化代码,它尽可能缩小了我提出的问题的范围。我没有'BaseClass'的单个实现,为了测试我的抽象类正在做它应该做什么,我必须测试一个空的实现。如果一个子类想要重写'toCanonical()'是免费的,并且会有自己的测试。 –

回答

1

为什么要测试抽象类?如果你真的想测试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的一些魔法。

祝你好运!

+0

我不明白这应该如何测试'toCanonical()'函数。 函数的作用是调用'asCanonical()',如果返回的值是equals(),则返回自身,否则返回生成的值。只有具体课程知道如何制定规范版本或知道两个课程是否相同。 我已经编辑我的问题,以接近我的实际代码(这当然是semplification)。 –

+0

检查我的编辑,我添加了其他部分,'toCanonical()'函数测试并修正了示例 –

+0

测试调用'toCanonical()',然后调用'asCanonical'。不管如何,你仍然应该能够实例化一个具体的类并对它进行测试。或者,使用匿名类。 –

相关问题