参数不同!想要:

时间:2019-10-21 05:09:03

标签: java unit-testing junit

我正在为下面的代码编写单元测试

public class Class1 {
   protected void execute(String a, String b) {
   try{
      process(a,b);
   }
   catch(Exception E){
       Class2.write(e,Class1.class.getSimpleName())
    }
  }

  private void process(String a, String b) {
     validate(a,b);
     // Doing some processing on a and b values
  }

 private void validate (String a, String b) {
    if(a==null || a.isEmpty() || b==null || b.isEmpty())
       throw new IllegalArgumentException("Input value cannot be null or empty");
 }

}

对于上面的代码,我试图编写一个涵盖异常用例的UT。以下是我的UT代码,

@Test

public void test1(){
 try {

            PowerMockito.mockStatic(Class2.class);
            PowerMockito.when(Class2.class, "write", Mockito.anyObject(), Mockito.anyString())
                    .thenCallRealMethod();
            Class1 class1 = new Class1();
            Class2.write(new IllegalArgumentException("Input value cannot be null or empty"),Class1.class.getSimpleClassName());
            PowerMockito.verifyStatic(Class2.class, VerificationModeFactory.times(1));
            class1.execute(Mockito.anyString(),Mockito.anyString());  
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
}

执行上面的测试时,我得到下面的异常

Argument(s) are different! Wanted:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}

Actual invocation has different arguments:
Class2.write{
java.lang.IllegalArgumentException:Input value cannot be null or empty,
Class1
}

有人可以帮助我解决此问题吗? 非常感谢您的帮助和时间

预先感谢

1 个答案:

答案 0 :(得分:0)

您的问题:

IllegalArgumentException不会将字符串消息用于相等性。测试字符串消息或类类型会更安全。我更希望测试检测类型而不是消息,因为字符串消息不应用于控制流,这是实现细节。

System.out.println(Objects.equals(
    new IllegalArgumentException(), 
    new IllegalArgumentException()));
// false

System.out.println(Objects.equals(
    new IllegalArgumentException().getClass(),
    new IllegalArgumentException().getClass()));
// true

所以要模拟这个,我将使用匹配器:

any(IllegalArgumentException.class), eq(Class1.class.getSimpleName())

有关您的设计的问题:

我将以反对该代码的结构为结尾,因为它不是围绕依赖注入构建的。您可以调用实例方法,而不是调用静态方法Class2::write

例如,创建接口:

public interface Writer {
    void write(Exception e, String source);
}

您现在可以重构该类,以提供两个ctor,一个可以接受任何编写器,并且默认为Class2

public class Class1 {
    private final Writer writer;

    public Class1() {
        this(Class2::write);
    }

    public Class1(Writer writer) {
        this.writer = writer;
    }

    protected void execute(String a, String b) {
        try {
            process(a,b);
        }
        catch (Exception E) {
            writer.write(e, Class1.class.getSimpleName());
        }
    }
    ...
}

使用此策略,您现在可以简单地创建Writer的实例模拟。这样避免了必须模拟为更改应用程序字节码的静态方法,并且还可以使您的类更加灵活,因为它现在可以支持许多不同的writer实现。修改应用程序字节码的任何操作都应非常谨慎地使用,例如替换静态方法调用,并不能真正验证代码的运行时执行。

我认为,大多数PowerMockito / PowerMock仅有助于验证并非出于可测试性/灵活性而构建的代码。您无需使用Mockito / EasyMock工具集以外的任何东西来构建结构良好的代码。有一些例外,但是应该非常谨慎地使用工具集。