Java 单元测试:替换被测私有方法

发布于 2024-08-17 02:17:21 字数 571 浏览 5 评论 0原文

运行 JUnit 测试时是否有任何方法可以替换私有方法中的逻辑?

一些背景知识:我们有一些与 OSGi 容器内的包交互的私有方法。这在单元测试中不可用,因此该方法将失败。

我们已经研究过 JMockIt,但方法替换功能似乎想要强制您替换类中相互调用的所有方法。

实现将是这样的:

public final doSomething() {  
    firstThing();
    secondThing();
}
private firstThing() {  
    // normal code
}
private secondThing() {  
    // code which is unavailable in a unit test
}

单元测试将指定 secondaryThing() 的新实现:

// replace secondThing() in impl with this secondThing()

private secondThing() {  
    // dummy code
}

// run tests

Is there any way of replacing the logic within a private method when running a JUnit test?

A bit of background: we have some private methods which interact with bundles within an OSGi container. This is not available in the unit test therefore the methods will fail.

We have looked at JMockIt but the method replace functionality seems to want to force you to replace all the methods in the class which call one another.

The implementation would be something like this:

public final doSomething() {  
    firstThing();
    secondThing();
}
private firstThing() {  
    // normal code
}
private secondThing() {  
    // code which is unavailable in a unit test
}

And the unit test would specify the new implementation of secondThing():

// replace secondThing() in impl with this secondThing()

private secondThing() {  
    // dummy code
}

// run tests

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

哆兒滾 2024-08-24 02:17:21

你当然可以用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 进行部分模拟。

You certainly can solve this situation with JMockit.
One way would be to define a "mock-up" class, for example:

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

        new ClassWhichDependsOnOtherBundles().doSomething();
    }
}

Only the secondThing() method in the mocked class will be replaced by JMockit.
The JMockit Expectations API could also be used, with partial mocking.

七禾 2024-08-24 02:17:21

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

You are coupling your implementation to the creation of the osgi object (doing it inside secondThing() or the class itself). If you passed the implementation into your class from the outside, you could use a stub/mock when testing instead.

尐籹人 2024-08-24 02:17:21

我还认为依赖注入可以解决这个问题。
如果您不想在项目中使用另一个框架,并且这是唯一会带来麻烦的地方,您可以为 secondaryThing 定义一个接口,并为其提供 2 个实现,一个用于原始代码,另一个用于单元测试的空实现。

I also think dependency injection would solve this problem.
If you don't want another framework in your project and this is the only place which makes trouble, you could define an interface for secondThing and have 2 implementations for that, one for the original code and an empty one for the unittest.

呆橘 2024-08-24 02:17:21

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

  • 将其设置为 protected / public 并在类似模拟的对象中覆盖它,
  • 移动功能从方法中进入一个可注入的辅助类(通过依赖注入)。然后模拟该帮助程序并将模拟注入到被测类中,而不是原始的帮助程序中。

解决方法可能是某种字节码操作技术,但我不推荐这样做。

My advice - redesign your application. If you want to change the behaviour of a private method:

  • make it protected / public and override it in a mock-like object
  • move the functionality out of the method into a helper class, which is injectable (via dependency injection). Then mock that helper an inject the mock into the class-under-test, instead of the original heloper.

A workaround may be some byte-code manipulation technique, but I don't recommend such.

筱果果 2024-08-24 02:17:21

有一个很好的存根模式

ProductionClass.java:BlaTest.java


public class ProductionClass {
  ...
  //default visibility to make it available for stubbing
  void firstThing(){...}

  ...
}

(与生产类相同的包):


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 修饰符。它们对于可测试性来说是邪恶的。仅在确实必要时才使用。

there is a nice stub-pattern

ProductionClass.java:


public class ProductionClass {
  ...
  //default visibility to make it available for stubbing
  void firstThing(){...}

  ...
}

BlaTest.java (same package as production class):


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

One different thing. I saw a final modifier in your sample-code. Beware of using final modifier. They are evil for testability. Only use if really really necessary.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文