使用 mockito 测试带有条件循环的 void 方法

Test a void method with conditioned loop using mockito

我有以下方法要求用户输入,直到有效的用户凭据为 entered.It,然后为该用户生成 ID 并设置已注册 =TRUE。

1.How 我可以从我的单元测试中检查局部变量 "registered" 的值吗?

2.How 我可以在我的测试中断言 while 循环执行到 "registered" 变为 TRUE 吗?

private void register() {

    boolean registered=false;
    while(!registered){
        try {
            String uname =this.read("User Name : ");
            char password[] = this.readPassword();
            String serverURL = this.read("Server URL : ");

            if(!uname.isEmpty() && password!=null && !serverURL.isEmpty()){ 

                registered=this.getUID(uname,password,serverURL);
            }
            if(registered==false)
                System.out.println("\nPlease verify your details and try again!\n");

        } catch (UnsupportedEncodingException e) {} 
        catch(Exception e){}    
    }
    System.out.println("Successful");
}

我遇到过 ArgumentCaptor 的用法来捕获要测试的方法用来调用另一个方法的变量。

e.g verify(mockObj).intArgumentMethod(argument.capture());

但是我没有将变量 "registered" 传递给任何其他方法,否则我会捕获它。

  1. 你不能
  2. 通过验证:
    • 循环不变量是registered为假。所以如果为真则不进入循环
    • 循环退出
      • 在身体开始时(在这种情况下是正确的)
      • 如果抛出一个 Throwable,它不会被 'catch(Exception e)' 捕获(在这种情况下它可能是任何东西)

无论如何 - 检查您的测试策略:

  • 一个函数有输入参数和输出参数
  • 输入应该是夹具的一部分:

    this.read("User Name : ")
    this.readPassword()
    this.read("Server URL : ")
    this.getUID(uname,password,serverURL) // this may also be viewed as output
    
  • 输出应该是断言的一部分

    System.out.println(...)
    

可以通过创建匿名子类来设置输入,例如

fixture = new YourClass {
  public String read(String prompt) {
    return mockedString;
  }
  ...
};

根据 Junit 规则,输出可以是 captured/asserted,例如StandardErrorStreamLog

此示例方法不需要 Mockito。

不要测试实施细节,测试给定特定输入的行为。如果 registered 变量应该是某种输出,那么它不应该是局部变量。

我喜欢的一种设计是使用方法对象,可以在对象创建时传递参数,一个对象方法可以有多个return值。

class Registrator {
    Registrator(...) { /* assigning needed field */ }

    void register() { /* logic that will mutate internal fields */ }

    boolean registered() { return registered; }
    long triesCount() { return triesCount; }
    // ...
}

并且可以调整代码以使用 Report 对象,注册方法可以在该对象上附加成功/失败/更多详细信息,例如原因等。

而且测试会更容易编写。