2010-01-07 128 views
3

运行JUnit测试时,是否有任何方法在私有方法内替换逻辑?一些背景:我们有一些私有方法与OSGi容器中的bundle进行交互。这在单元测试中不可用,因此这些方法会失败。Java单元测试:替换测试中的私有方法

我们已经看过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 

回答

6

你当然可以用JMockit来解决这种情况。 一种方法是定义一个“模拟”类,例如:

public class MyTest 
{ 
    @Test 
    public void testDoSomething() 
    { 
     new MockUp<ClassWhichDependsOnOtherBundles>() 
     { 
      @Mock 
      void secondThing() 
      { 
       // do anything here 
      } 
     }; 

     new ClassWhichDependsOnOtherBundles().doSomething(); 
    } 
} 

只有在嘲笑类的secondThing()方法将通过JMockit代替。也可以使用JMockit Expectations API,并使用部分模拟。

+0

这看起来很有趣。在某些情况下,我在EasyMock中忽略了这一点,只有一个完整的类可以被嘲笑。 – 2010-01-08 03:19:36

+0

是的,“JMockit Annotations”API总是基于用户指定的'@ Mock'方法进行部分模拟。 EasyMock API类似于“JMockit Expectations”API;他们都支持部分模拟,但EasyMock要求方法的名称在字符串中模拟(对于* partial * mocking,就是这样)。 – 2010-01-08 12:55:43

+0

我认为这最完全地回答了问题,虽然我很欣赏建议的解耦解决方案并可能重新设计 – 2010-01-12 13:49:31

1

我的建议 - 重新设计应用程序。如果你想改变一个private方法的行为:

  • 使其protected/public和覆盖它的模拟状物体
  • 移动功能的方法的成辅助类,这是注射(通过依赖注入)。然后嘲笑那个帮手,把模拟注入被测试的类,而不是原来的heloper。

解决方法可能是一些字节码操作技术,但我不建议这样做。

+0

那么,甚至不应该使用'java.lang.reflect.Proxy'?它操作字节码,你知道... – 2010-01-08 01:28:45

+0

不,它的作品'通过接口'只有 – Bozho 2010-01-08 06:30:28

3

您正在将您的实现耦合到创建osgi对象(在secondThing()或类本身内部执行它)中。如果您将实现从外部传递到您的类中,则可以在测试时使用存根/模拟。

+0

我也认为这是正确的修复。在我看来,任何与外界交互的组件都应该是一个通过构造函数/工厂参数(或者其他可指定的)提供的对象,而不是硬编码到类中。 (很显然,你必须有一个“离开”的底部,它固有地指向外部世界(例如服务器名称),但是你测试的方式不同)。然后,除了这些叶子之外,你的程序的任何子部分都可以在一个完整的受控宇宙。 – 2010-01-08 02:19:43

2

我也认为依赖注入可以解决这个问题。 如果你不想在你的项目中使用另一个框架,并且这是造成问题的唯一地方,那么你可以为secondThing定义一个接口,并且有2个实现,其中一个用于原始代码,另一个用于unittest。

-1

有一个很好的存根图案

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修饰符。他们对可测性是邪恶的。只有在真的有必要时才会使用。

+0

'final'关键字是Java中面向对象设计的重要机制。 对于JMockit它没有区别,所以尽可能多地使用它。 – 2010-01-08 01:23:51

+0

yes'final'是一个java语言特性,有它的用途。但对我来说,如果人们将它用作默认修饰符,则它是一种反模式。我经常遇到这样的情况,我必须根据遗留代码进行测试。最终被使用了,我注定要掐死。在我的情况下,模拟框架不是正确的工具( - >由于某些原因,我不得不建立自己的手工编码存根)。 – 2010-01-08 02:59:35