启用方法的单元测试,使其受到保护

Enable unit testing of a method my making it protected

让我解释一下问题场景。我的任务是修改如下所示的复杂函数 testFunc()。

public String testFunc() {
    String a = func1();
    String b = func2(a);
    String c = func3(b);
    return c;
} 
private String func1(){
    return "hello";
}
private String func2(String p1){
    return "a" + p1;
}
private String func3(String p1){
    return "b" + p1;
}

我修改了 testFunc 方法并包含了我的逻辑。但作为一名优秀的开发人员,我希望为我编写的额外逻辑编写单元测试。 但是我没有时间也没有耐心去测试整个方法。我只想测试我添加的逻辑。

 public String testFunc() {
     String a = func1();
     String b = func2(a);
     String c = func3(b);
     String d = func4(c)
     return d;
 }
 protected final String func4(String p1) {
     return "1" + p1;
 }

将方法 func4 设置为 protected final 对我来说是否有意义,以便我可以对逻辑进行彻底的单元测试。还是编码习惯不好?

我会把它打包成私有的,这比保护更严格。如果您已经在您的项目中使用 Guava,您甚至可以使用 @VisibleForTesting 注释该方法以明确您的意图:

@VisibleForTesting final String func4(String p1) {
  //...
}

归根结底,一个包的私有方法没有暴露给外界,也不是你的API的一部分,所以它不会破坏封装。

更改单元测试方法的可见性不是一个好的做法。

如果您在项目中使用 Spring,则可以使用 ReflectionTestUtilsorg.springframework.test.util.ReflectionTestUtils(反射简化)来访问私有方法和属性。

如果您不想使用 Spring,那么您必须进行 java 反射。

这是一种不好的做法。 Private/protected 方法应该可以通过调用它们的 public 方法进行测试。通过将访问修饰符更改为 package-private,您实质上是将实现细节公开给其他 类,从而减少了封装。

如果这很难,您应该考虑重新设计 public API。

好吧,我要开始说问题中的两句话对我来说有点矛盾:

  1. But being a good developer, I wish to write unit tests for the extra logic i have written.

  1. But i do not have time nor the patience to test the entire method. I wish to test only the logic i have added.

你要么是一个优秀的开发人员并且自上而下地做事,要么没有耐心,那么没有什么能真正帮助你。

为了回答你的问题,公开不能重复使用的私有方法是非常糟糕的做法,因此不是一个好的方法。虽然您所做的更改不能脱离 class 的上下文,但您进行此更改的原因是:

class MyClass {
   public String testFunc() {
      String a = func1();
      String b = func2(a);
      String c = func3(b);
      return c;
   } 
   private String func1(){
      return "hello";
   }
   private String func2(String p1){
      return "a" + p1;
   }
   private String func3(String p1){
      return "b" + p1;
   }
}

因此,您可以通过几种可能的方式使用新功能扩展此逻辑,例如,您想要 decorate 初始 class 一个新功能并将新功能添加到它,例如:

 class MyNewClass extends MyClass{

   private MyClass delegate;

   public MyNewClass(MyClass delegate) {
       this.delegate = delegate;
   }

   public String testFunc() {
      String c = delegate.testFunc();
      return func4(c);
   } 

   private final String func4(String p1) {
      return "1" + p1;
   }
}

现在您可以通过 mocking MyClass 实例编写单元测试并仅检查新添加的逻辑。

另一种可能的替代方法是将新功能提取到它自己的 class 中,并仅为它编写测试。