在运行JUnit测试时,有没有办法在私有方法中替换逻辑?
一些背景知识:我们有一些私有方法可以与OSGi容器中的bundle进行交互。 这在单元测试中不可用,因此方法将失败。
我们已经看过JMockIt但是方法替换功能似乎想要强制你替换类中相互调用的所有方法。
实现将是这样的:
public final doSomething() {
firstThing();
secondThing();
}
private firstThing() {
// normal code
}
private secondThing() {
// code which is unavailable in a unit test
}
单元测试将指定secondThing()的新实现:
// replace secondThing() in impl with this secondThing()
private secondThing() {
// dummy code
}
// run tests
你当然可以用JMockit来解决这个问题。
一种方法是定义"模拟"类,例如:
public class MyTest
{
@Test
public void testDoSomething()
{
new MockUp()
{
@Mock
void secondThing()
{
// do anything here
}
};
new ClassWhichDependsOnOtherBundles().doSomething();
}
}
只有模拟类中的secondThing()方法才会被JMockit替换。
也可以使用JMockit Expectations API,部分模拟。
这看起来很有趣。在某些情况下,我在EasyMock中错过了这个,在那里afaik只有一个完整的类可以被嘲笑。
是的,"JMockit Annotations"API总是根据用户指定的@Mock方法进行部分模拟。 EasyMock API类似于"JMockit Expectations"API;虽然EasyMock需要在字符串中模拟方法的名称(对于部分模拟,也就是说),但它们都支持部分模拟。
我认为这完全回答了所提出的问题,尽管我很欣赏建议的去耦解决方案并且可能会重新设计
您将实现与osgi对象的创建耦合(在secondThing()或类本身内部执行)。如果您从外部将实现传递到您的类中,则可以在测试时使用存根/模拟。
我也认为这是正确的解决方案。在我看来,任何与外部世界交互的组件都应该是通过构造函数/工厂参数(或其他可指定的)提供的对象,而不是硬编码到类中。 (显然你必须有"离开"底部,其中固有地指向外部世界(例如服务器名称),但是那些你测试的不同。)然后你的程序的任何子部分除了那些叶子之外可以在一个完全测试控制宇宙。
我也认为依赖注入会解决这个问题。
如果您不希望项目中有另一个框架,并且这是唯一一个出现问题的地方,您可以为secondThing定义一个接口,并为其创建2个实现,一个用于原始代码,另一个用于unittest。
我的建议 - 重新设计你的应用程序。如果要更改private方法的行为:
使它protected / public并在类似于模拟的对象中覆盖它
将函数从方法中移出到一个可注入的辅助类中(通过依赖注入)。然后模拟那个帮助者将模拟注入到被测试的类中,而不是原始的heloper。
解决方法可能是一些字节码操作技术,但我不建议这样做。
那么,甚至不应该使用java.lang.reflect.Proxy?它确实操纵字节码,你知道......
不,它只能通过"界面"工作
有一个很好的存根模式
ProductionClass.java:
public class ProductionClass {
...
//default visibility to make it available for stubbing
void firstThing(){...}
...
}
BlaTest.java(与生产类相同的包):
public class BlaTest {
@Test
void test_xx(){
//use stubbed impl
ProductionClass sut = new ProductionClassStubbed();
sut.doSomething();
}
}
class ProductionClassStubbed extends ProductionClass{
@Override
void firstThing(){
//stub out fill in what you want (like dummy behaviour)
}
}
一个不同的事情。我在示例代码中看到了一个final修饰符。注意使用final修饰符。它们对于可测性是邪恶的。只有在真的有必要时使用。
final关键字是Java中OO设计的重要机制。对于JMockit它没有任何区别,所以尽可能多地使用它。
是'final'是一种java语言功能,有其用途。但对我来说,如果人们将它用作默认修饰符,那么这是一种反模式。我经常遇到案例,我必须根据遗留代码进行测试。最终被使用,我注定要顽固。在我的情况下,模拟框架不是正确的工具( - >由于某些原因我必须建立自己的手工编码存根)。