如何对副作用逻辑进行单元测试

How to unit test side effecting logic

我有以下代码

public class Component extend Framework {
  private Integer someInt;  
  private String someString;    
  public Integer getSomeInt() {
    return someInt;
  }    
  public String getSomeString() {
    return someString;
  }
  public void activate() {
     Integer tempInt = (Integer)getProperties("key"); // From Framework
     if (tempInt == null) {
         tempInt = (Integer)getRequest().getProperties("key"); // From Framework        
     }

     if(tempInt == null)
        tempInt = (Integer)getBind().getProperties("key"); // From Frameowrk 
     someString = makeServiceCall("http://.....?key=tempInt");
  }
}

基本上框架调用activate()方法是为了访问框架的内部状态来构造Component对象。 activate() 有点像 Component 对象的 setter。 如果我要对上面的代码进行单元测试,最好的方法是什么而不需要框架 运行?

一种方法是模拟组件 class 并存根 super.getProperties... 调用,但是如果我们模拟有问题的 class,测试的意义何在开始?

我认为这可能是糟糕设计的味道。也许您应该考虑组合而不是继承?它将更易于测试并且更 objective。为什么 Component 继承自 Framework class?

public class Component {
  private int someInt;  
  private String someString;  
  private Framework framework;  

  public Component(Framework framework) {
    this.framework = framework
  }

  public int getSomeInt() {
    return someInt;
  }    
  public String getSomeString() {
    return someString;
  }
  public void activate() {
     int tempInt = framework.getProperties("key"); // From Framework
     if (tempInt == null) {
         tempInt = framework.getRequest().getProperties("key"); // From Framework        
     }

     if(tempInt == null)
        tempInt = framework.getBind().getProperties("key"); // From Frameowrk 
        someString = makeServiceCall("http://.....?key=tempInt");
  }
}

使用 Mockito。 监视组件 class 并模拟方法 getRequest() 和 getBind()。 最后,直接从单元测试中调用 activate() 方法。

我将展示如何测试一种边缘情况

void testServiceCallWithNoKeyPropertyFound() {
  Component componentUnderTest = new Component() {
    Integer getProperties(String key) {
       return null; // property should not be found
    }

    Request getRequest() {
      return new Request(...); //this request should not contain a property named "key", 
    }

    Bind getBind() {
      return new Bind(...); //this bind should not contain a property named "key"
    }

    String makeServiceCall(String url) {
      if (url.endsWith("null")) {
        return success;
      }
      throw new AssertionError("expected url ending with null, but was " + url);
    }

  };
  componentUnderTest.activate();
  assertThat(componentUnderTest.getSomeString(), equalTo("success"));
}

使用Mockito (spys) 可以使这个例子更加简洁。但这将隐藏如何设计测试的原则。

还有一些边缘情况:

void testServiceCallWithPropertyFoundInComponent() ...
void testServiceCallWithPropertyFoundInRequest() ...
void testServiceCallWithPropertyFoundInBind() ...