【问题标题】:Junit Test Case for private method私有方法的 Junit 测试用例
【发布时间】:2020-05-29 01:09:50
【问题描述】:

我有一个下面的课程。我是编写junit测试的新手。我需要为此编写测试用例。 如何在测试类中为 startSchemaMaintenance 方法编写测试方法,因为它正在调用没有参数的私有方法?

public class SchemaMaintenance {

    //Loading statuses overview
    // NOT_STARTED = 0
    // START_LOADING = 1
    // IN_PROGRESS = 2
    // COMPLETED = 3
    // LOADING_ERROR = 4
    private static volatile Integer loading_status = 0;

    public void startSchemaMaintenance() throws Exception {
        if (checkLoadingStatus() == 1) {
            doSchemaMaintenance();

            loading_status = 3;
        }
    }

    private void doSchemaMaintenance(){
        //Do something....
    }

    private int checkLoadingStatus() throws Exception {
        if (loading_status==0 ||loading_status == 2) {
            synchronized (loading_status) {
                if (loading_status==0) {
                    loading_status = 2;
                    return 1;
                }else if(loading_status == 2) {
                    while(loading_status == 2);

                    if((loading_status == 4)){

                        throw new Exception("status = " + 4);
                    }
                }else if(loading_status == 4) {

                    //log.error(generateErrorMessage());
                    throw new Exception("status = " + 4);
                }
            }
        }else if((loading_status == 4)){
            //log.error(generateErrorMessage());
            throw new Exception("status = " + 4 );
        }

        return loading_status;

    }
}

【问题讨论】:

  • 通过测试调用它的方法来测试它。
  • 你的意思是调用 startSchemaMaintenance 的方法
  • 没有。您知道您发布的内容不是完全有效的 Java,对吧?您在测试中设置 loadingStatus 的值并调用您的方法
  • 不是一个有效的 java??
  • 类模式维护是无效的 Java。您的文件的实际名称是什么?

标签: java junit mockito


【解决方案1】:

这是一种对我有用的方法。

  1. 在您的班级中创建一个公共的“测试员”内部班级。
    • 如果您正在测试内部类,“测试人员”仍需要在外部类上。
  2. 测试人员是您要测试的私有方法的代表。
  3. 向你的类添加一个方法来获取测试器的实例
  4. 在您的测试方法中获取测试器的实例并
  5. 通过适当的测试器实例方法调用您的私有方法。

根据需要的方法和内部类的访问等,会有不同的变化。

此示例不能有静态“测试器”,因为我们需要测试非静态内部类中的方法。

//  Outer owner class
//
public class OwnerClass Class {
    
    private class InnerClass {
            : 
            
        //  method I'm testing
        //
        private void doSomething)( String name ){
            ...
        }
    }
    
        :
    
    //  Tester class
    //
    public class InnerClassTester {
        
        private final InnerClass        innerClass    = new InnerClass();
            :
            
        //  test invocatiion
        //
        private void doSomething)( String name ){
            
            innerClass.doSomething)( name )
        }
    }

    //  Get an instance of the tester
    //    
    public InnerClassTester getInnerClassTester() {
        return new InnerClassTester();
    }

在您的单元测试中...

@Test
public void doSomethingTest() throws Exception {
    
    OwnerClass ownerClass = new OwnerClass();
    innerClassTester      = ownerClass.InnerClassTester();
            :
            
    //  Test the private method
    //
    innerClassTester.doSomething( "123456" );
}

适用于调试器。您需要根据您的访问权限灵活地定义测试者和测试者获取者。测试人员也可能是protected,我没试过。一切顺利。

【讨论】:

    【解决方案2】:

    通常,当您无法测试您的代码时,您应该考虑重写它,使其可测试。私有方法不能在你的类之外以常规方式调用,甚至在 JUnit 测试中也不能。

    您可以做一些事情来使其可测试:

    1. 重写代码以将方法拆分为更小的方法。应始终在适当的情况下完成。

    2. 您可以将方法包设为私有(删除 private 修饰符),以便必须在同一个包中的测试类可以访问它。

    3. 您可以使方法受到保护,这样您就可以在测试中继承您的类,并通过继承的类间接调用该方法。

    4. 您可以调用一个方法,该方法调用该方法,您想要测试并做出相应的断言。

    如果您倾向于将方法的可见性更改为包私有或受保护,您可以将您的方法注释为 @VisibleForTesting,以便 Sonar 和其他团队成员等工具知道为什么它不是私有的。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      您的私有方法根​​据内部私有变量 loading_status 的值工作,因此在您的测试中,您应该能够更改该变量。 为此,您可以执行以下操作:

      package com.test
      
      import com.test.SchemaMaintenance;
      import org.junit.Test;
      import org.mockito.internal.util.reflection.Whitebox;
      
      public class SchemaMaintenanceTest {
      
          @Test
          public void TestSchema() throws Exception {
              SchemaMaintenance schema = new SchemaMaintenance();
              Whitebox.setInternalState(schema,"loading_status",2);
              schema.startSchemaMaintenance();
          }
      
      }
      

      假设您应该在项目中包含 mockito 依赖项:

      例如Maven

      <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.6.4</version>
      </dependency>
      

      【讨论】:

      • 当我添加 verify(dataBaseSchemaMaintenance, times(0)).doSchemaMaintenance(); 时,在 SchemaMaintenanceTest 的类层次结构中找不到实例字段名称 loading_status;
      • 可以分享整个单元测试代码和stacktrace异常输出吗?
      • 另外,当我调试 SchemaMaintenance 类时,它也没有将 loading_status 的值显示为 2,它仍然是 0。
      • 您使用的是我在回复中提供的确切代码吗?我已经对其进行了测试,并且可以正常工作。具体来说,当我使用共享的代码进行调试时,loading_status 变量设置为 2。
      【解决方案4】:

      首先有几点:

      1. 为什么 loading_status 是静态的?基本上这个类的 2 个实例将使用相同的 loading_status 变量。 loading_status 不应该是静态的,或者 checkLoadingStatus 也应该是静态的。
      2. loading_status 的名字很糟糕。如果你想让它保持静态,你应该把它命名为 LOADING_STATUS。如果它不是静态的,我们将其命名为 loadingStatus。这就是 Java 约定。
      3. 您应该为 loading_status 创建适当的枚举类型,而不是整数。
      4. while (loading_status == IN_PROGRESS); 只是不好的做法。至少你可以做的是:while(loading_status == IN_PROGRESS) { Thread.sleep(100); }; 但是超时是更好的做法。类似于:
      long timeoutMillis = 5000L; // Should come from a configuration or something
      long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
      while (loadingStatus == IN_PROGRESS) {
          long remainingMillis = end - System.currentTimeMillis();
          if (remaining <= 0) {
              // Timeout while loading
              loadingStatus = LOADING_ERROR;
              break;
          }
          Thread.sleep(50);
      }
      // ...
      

      最后我猜有些东西会将 loadingStatus 更新为已完成或其他东西。 我猜你也在那里使用同步。 如果加载发生在 2 个不同的线程上,那么您将最终陷入死锁。 如果checkLoadingStatus先进入同步块,那么加载完成后,加载线程将永远无法进入同步块,因为checkLoadingStatus正在持有锁。

      最后但并非最不重要的是回答您的问题,如果您想保持您的方法私有,您可以通过反射调用它,但这又是一种不好的做法。 通常,您应该避免对私有方法进行单元测试,并对调用它的方法进行单元测试。 但是,如果您确实需要对特定方法进行单元测试,请将其设为包私有而不是私有,然后您可以在包含您的方法的类所在的同一包中创建单元测试。您可以在该方法中添加注释,说明它仅对单元测试可见。在这种情况下您的代码结构示例:

      src
      +-- main
      +-- +-- com/app/MyClass.java
      +-- test
          +-- com/app/MyClassTest.java // this test class will able to access package-private methods in MyClass
      

      【讨论】:

      • 在测试 startSchemaMaintenance 方法时如何修改加载状态变量??
      • 你没有。您必须稍微重构您的代码以使其更具可测试性。我不是测试驱动开发的忠实信徒,但在您的情况下,它可能有助于以您考虑将拥有哪些测试用例的方式构建代码。我已经用 Notepad++ 编写了这个并且没有测试过,但是你明白我的意思:pastebin.com/uiSydTXU
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-02
      • 1970-01-01
      • 2016-05-06
      • 2014-06-05
      • 2020-08-05
      • 1970-01-01
      相关资源
      最近更新 更多