【问题标题】:Junit for loop execution countJunit for 循环执行计数
【发布时间】:2018-06-29 13:32:12
【问题描述】:

我需要运行给定次数的代码。我想知道如何对这个循环进行单元测试

public void executeCode(int iterations){

  for (int i = 0; i < iterations; i++)
      try {
           callToCode(parms...);
      } catch(Exception e) {
           // handle exception 
           i--;
      }
}

callToCode 此方法具有 100% 的行覆盖率,但其中一个要求是此方法在给定的迭代次数内执行。

我可以使用实例变量来跟踪执行,但是对此类循环进行单元测试的最佳方法是什么?

我只能使用 Junit 而不能使用其他库

【问题讨论】:

  • 使用 Mockito 并检查是否正在调用该方法。 Mockito 允许您测试行为,JUnit 允许您测试状态。
  • 这里我对使用 Mockito 或任何其他框架有限制。我应该提到这个问题
  • 很明显,当您在异常情况下减少i 时,您的方法总是成功执行给定的迭代次数。不过,这可能需要无限长的时间。
  • “处理异常”是否有需要 JUnit 覆盖的代码?
  • 如果你可以使用 JUnit 5,参数化测试就是这样做的:junit.org/junit5/docs/current/user-guide/…

标签: java junit junit4


【解决方案1】:

如果可以,重构代码以将依赖调用建模为对注入成员的调用。

public void executeCode(int iterations, Callable callable) {
  for (int i = 0; i < iterations; i++)
      try {
         callable.call();
      } catch(Exception e) {
         // handle exception 
         i--;
      }

}

然后在 JUnit 中,您可以传递您选择的计数可调用对象。上面的代码只是众多示例之一,您还可以在测试类的构造函数中注入依赖项,例如

public class MyClass {
   private Dependency dep;
   public MyClass(Dependency dep) {
      this.dep = dep;
   }

   public void executeCode(int iterations, Callable callable) {
       //...
       dep.callToCode(parms...);
   }
}

然后再次在 JUnit 中,您可以注入一个不同的类来计算调用。

最后如果 callToCode(parms...);是你类中的另一个方法,你也可以重写它来计数,像这样:

public class MyClassTest {
    @Test
    public void testCountLoop() {
        MyClass class = new MyClass() {
            int calls;
            @Override
            public void callToCode(parms...) {
                calls++;
            }
        }
        // ... run and assert calls
    }
}

【讨论】:

    【解决方案2】:

    我会将 callToCode 提取到另一个类,例如

    public class Caller{
        public void callToCode(String someparam){//Some logic}
    } 
    

    那么你的循环将在另一个类中,在构造函数中有依赖:

     public class OuterClass {
        Caller caller;
        public OuterClass(Caller caller){
            this.caller = caller;
        }
        public void executeCode(int iterations){
    
          for (int i = 0; i < iterations; i++)
             try {
                  caller.callToCode(parms...);
             } catch(Exception e) {
                  // handle exception 
                  i--;
             }
          }
    }
    

    然后在您的测试中,您可以使用 Mockito.verify(使用任何参数调用 2 次):

    public class Test{
        OuterClass target = ...
    
        Caller mockeCaller = Mockito.mock(Caller.class);
    
        @Test
        public void test(){
           Mockito.verify(mockCaller, Mockito.times(2)).callToCode(Mockito.any());
        } 
    }
    

    使用 ArgumentCaptor,您还可以验证方法调用的参数。大多数情况下,如果您不能正确地对代码进行单元测试,那就是设计问题的味道。通过分离代码的不同抽象级别,您可以分别测试每个级别,您将获得更清晰、可读性更高的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多