有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java更改单元测试的私有方法行为

这不是我的代码的样子,但它将接近我想要的示例

class Phone {
    public makeCall(String number) {
        addEntryToCallLog(number)
        //eventually make a call
    }

    private void addEntryToCallLog(String number) {
        //make database calls
    }
}


class PhoneTest {
    Phone phone
    @Test
    public void canMakeACall() {
        //mock the behaviour of phone.addEntryToCallLog to prevent database exceptions
        phone.makeCall("1223");
        //Assert somehow that call was made << --IGNORE this NOT IMPORTANT
    }
}

我想在单元测试中测试“makeCall”,但当我这样做时,代码会抛出一些数据库异常,从而破坏我的测试。 我认为能够测试模仿java私有方法的行为是合理的,因为这允许所有测试的行为一致

我过去使用过groovy,通过它,我可以使用spock来模拟私有方法的行为。我也可以使用元类为我创建的实例做同样的事情。但在java中,似乎没有一种直接的方法可以做到这一点

我也尝试过mockito和power mockito,但它们允许我更改私有方法的返回值,但不允许我更改行为

这似乎是一件很明显的事情,有人已经处理过了。我想我错过了什么。但这是什么呢


共 (1) 个答案

  1. # 1 楼答案

    我的建议是将可视性从private放宽到包私有,可能带有^{} annotation/** Visible for testing. */注释。在这一点上,您可以使用^{}来“部分模拟”您的内部方法,正如DTing在另一个答案中提到的那样,以替换实际实现的行为

    这里的主要问题是Mockito最擅长将接口转换为存根实现。然而,私有方法非常有意地不是接口的一部分,因此Java通过一些普通Mockito依赖的反射特性使它们成为harder to access。(在幕后,Mockito正在创建您要模拟的类的子类,子类通常不能覆盖私有方法。)

    您还应该记住,单元测试最好是将类作为一个单元进行测试,而不必深入研究实现细节。如果说您试图替换私有方法的行为,则表明您的类需要一个尚不存在的seam。您可以考虑重构依赖注入:

    class Phone {
      interface CallLogger {
        void addEntry(String number);
      }
    
      private final CallLogger logger;
    
      public Phone() {
        this(new DefaultCallLogger());
      }
    
      /** Visible for testing. */
      Phone(CallLogger logger) {
        this.logger = logger;
      }
    
      /* ... */
    }
    

    。。。这样,您就可以选择任何想要的调用记录器,包括在生产中访问数据库、在测试中伪造或模拟它,或者在将来用批处理版本替换它。同样地,对于将addEntryToCallLog包私有化或protected:您指出子类可以改变它们向调用日志添加条目的方式,这正是您在测试中试图做的,并且承认这将帮助Java VM和代码的读者理解行为在什么时候可以改变


    综上所述,如果您只想使用PowerMockito替换实现,那么可以使用PowerMockito的^{}^{}为您的私有方法提供不同的行为。只需记住,您有机会改进设计,并花更少的时间和精力解决实现细节