Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 依赖项反转、猴子补丁,两者都适用于单元测试吗?_Unit Testing_Language Agnostic_Stub_Dependency Inversion - Fatal编程技术网

Unit testing 依赖项反转、猴子补丁,两者都适用于单元测试吗?

Unit testing 依赖项反转、猴子补丁,两者都适用于单元测试吗?,unit-testing,language-agnostic,stub,dependency-inversion,Unit Testing,Language Agnostic,Stub,Dependency Inversion,这些都是人为设计的示例,大部分都是JavaScript,但问题是语言不可知的,通常集中在单元测试上 代码基 单元测试(猴子补丁样式) 问题 测试与实现紧密耦合 猴子补丁在某些语言中可能不可能实现 未测试的副作用依赖项更改不会破坏现有测试(即静默和未修补的依赖项) 单元测试(依赖项反转/注入样式) 我理解依赖项反转/注入、存根、伪造、模拟等概念,但在现实世界中的多级函数调用中还没有遇到它。也就是说,到目前为止,我看到的示例只显示了一个呼叫者和一个被呼叫者 这就是我推断的两个以上的层次: //

这些都是人为设计的示例,大部分都是JavaScript,但问题是语言不可知的,通常集中在单元测试上

代码基 单元测试(猴子补丁样式) 问题
  • 测试与实现紧密耦合
  • 猴子补丁在某些语言中可能不可能实现
  • 未测试的副作用依赖项更改不会破坏现有测试(即静默和未修补的依赖项)
单元测试(依赖项反转/注入样式) 我理解依赖项反转/注入、存根、伪造、模拟等概念,但在现实世界中的多级函数调用中还没有遇到它。也就是说,到目前为止,我看到的示例只显示了一个呼叫者和一个被呼叫者

这就是我推断的两个以上的层次:

// Refactored code

function func1() {                                                               
  return func2(func3, func4, func5, 7, 4);                                       
}                                                                                

function func2(dependent1, dependent2, dependent3, param1, param2) {             
  return param1 + param2 + dependent1(11) + dependent2(dependent3, 14, 2, 8);    
}                                                                                

function func3(param1) {                                                         
  return param1 + 5;                                                             
}                                                                                

function func4(dependent1, param1, param2, param3) {                             
  return dependent1(6, 1) + param1 + param2 + param3;                            
}                                                                                

function func5(param1, param2) {                                                 
  return param1 + param2;                                                        
}

// Tests

function func5_stub(param1, param2) {
  return 5;
}

assert(func4(func5_stub, 1, 2, 3) == 11);
问题
  • 测试与实现紧密耦合
  • 顶级函数中充斥着未使用的参数(这些参数只是向下传递)
  • 如何测试最高级别的函数(本例中为func1)?每次反转依赖项时,都会无意中创建另一个级别
问题: 当在现实世界中进行单元测试(即深层次的函数调用)时,处理清除依赖关系的最佳方法或策略是什么?

有许多优点,这里相关的一点是,它使测试变得超级容易/干净,因为很容易实现依赖关系反转/注入

您不需要像编写依赖倒置函数那样使用函数式编程语言,所以现在还不要跑掉。 您的编程语言只需要函数和间接引用函数(指针/引用)的能力

我认为最好的解释策略的方法是从一些例子开始:

动态类型化示例(JavaScript)
/*
*这个函数现在对于单元测试来说是微不足道的。
*/
函数depInvFunc(param1,param2,depFunc1,depFunc2){
//做点什么
var result1=depFunc1(param1);
var result2=depFunc2(param2);
如果(结果1%15==0){
结果1*=4;
}
返回result1+result2;
}
/*
*此函数可以在任何地方使用,而不是使用上述函数
*并且必须始终指定相关参数函数。
* 
*此函数不需要测试(也不应该测试),因为它具有
*没有逻辑,它只是一个简单的函数调用。
*
*将这些依赖于包装器的函数定义为配置
*函数(如配置文件)。您的配置没有单元测试,
*您只需自己手动检查它们。
*/
函数wrappedePinvFunc(参数1,参数2){
返回depinfunc(param1,param2,importedFunc1,importedFunc2);
}
静态类型化示例(Java) DepInvFunc.java:

public class DepInvFunc {

   public int doDepInvStuff(String param1, String param2, Dep1 dep1, 
                            Dep2 dep2) {
      // do some stuff

      int result1 = dep1.doDepStuff(param1);
      int result2 = dep2.doDepStuff(param2);

      if (result % 15 == 0) {
         result1 *= 4;
      }

      return result1 + result2;
   }

}
public class WrappedDepInvFunc {

   public int wrappedDoDepInvStuff(String param1, String param2) {
      Dep1 dep1 = new Dep1();
      Dep2 dep2 = new Dep2();

      return DepInvFunc().doDepInvStuff(param1, param2, dep1, dep2);
   }

}
WrappeddedPinvFunc.java:

public class DepInvFunc {

   public int doDepInvStuff(String param1, String param2, Dep1 dep1, 
                            Dep2 dep2) {
      // do some stuff

      int result1 = dep1.doDepStuff(param1);
      int result2 = dep2.doDepStuff(param2);

      if (result % 15 == 0) {
         result1 *= 4;
      }

      return result1 + result2;
   }

}
public class WrappedDepInvFunc {

   public int wrappedDoDepInvStuff(String param1, String param2) {
      Dep1 dep1 = new Dep1();
      Dep2 dep2 = new Dep2();

      return DepInvFunc().doDepInvStuff(param1, param2, dep1, dep2);
   }

}
Dep1.java:

public class Dep1 {

   public int doDepStuff(String param1) {
      // do stuff
      return 5;
   }

}
Dep2.java:

public class Dep2 {

   public int doDepStuff(String param1) {
      // do stuff
      return 7;
   }

}

因此,这种方法(在使用动态类型语言时)的唯一缺点是,由于您可能间接调用函数,您(和/或您的IDE)可能无法检测到提供给这些间接函数调用的无效参数

当使用静态类型语言的编译时类型检查时,这个问题在很大程度上被克服了

这种方法避免了脆弱且可能不可用的monkey修补的需要,并且不会出现必须将依赖函数的参数从高级函数向下传递到低级函数的问题


Tldr:将所有(或尽可能多的)逻辑放入依赖反转函数(通过依赖注入很容易测试)中,并将它们封装在无逻辑/最小函数(不需要测试)中


我刚刚从以下两个来源获得灵感后想到了这一策略:

  • 的容器组件设计模式
有许多优点,其中一个与此相关的优点是它使测试变得非常简单/干净,因为它很容易实现依赖项反转/注入

您不需要像编写依赖倒置函数那样使用函数式编程语言,所以现在还不要跑掉。 您的编程语言只需要函数和间接引用函数(指针/引用)的能力

我认为最好的解释策略的方法是从一些例子开始:

动态类型化示例(JavaScript)
/*
*这个函数现在对于单元测试来说是微不足道的。
*/
函数depInvFunc(param1,param2,depFunc1,depFunc2){
//做点什么
var result1=depFunc1(param1);
var result2=depFunc2(param2);
如果(结果1%15==0){
结果1*=4;
}
返回result1+result2;
}
/*
*此函数可以在任何地方使用,而不是使用上述函数
*并且必须始终指定相关参数函数。
* 
*此函数不需要测试(也不应该测试),因为它具有
*没有逻辑,它只是一个简单的函数调用。
*
*将这些依赖于包装器的函数定义为配置
*函数(如配置文件)。您的配置没有单元测试,
*您只需自己手动检查它们。
*/
函数wrappedePinvFunc(参数1,参数2){
返回depinfunc(param1,param2,importedFunc1,importedFunc2);
}
静态类型化示例(Java) DepInvFunc.java:

public class DepInvFunc {

   public int doDepInvStuff(String param1, String param2, Dep1 dep1, 
                            Dep2 dep2) {
      // do some stuff

      int result1 = dep1.doDepStuff(param1);
      int result2 = dep2.doDepStuff(param2);

      if (result % 15 == 0) {
         result1 *= 4;
      }

      return result1 + result2;
   }

}
public class WrappedDepInvFunc {

   public int wrappedDoDepInvStuff(String param1, String param2) {
      Dep1 dep1 = new Dep1();
      Dep2 dep2 = new Dep2();

      return DepInvFunc().doDepInvStuff(param1, param2, dep1, dep2);
   }

}
WrappeddedPinvFunc.java:

public class DepInvFunc {

   public int doDepInvStuff(String param1, String param2, Dep1 dep1, 
                            Dep2 dep2) {
      // do some stuff

      int result1 = dep1.doDepStuff(param1);
      int result2 = dep2.doDepStuff(param2);

      if (result % 15 == 0) {
         result1 *= 4;
      }

      return result1 + result2;
   }

}
public class WrappedDepInvFunc {

   public int wrappedDoDepInvStuff(String param1, String param2) {
      Dep1 dep1 = new Dep1();
      Dep2 dep2 = new Dep2();

      return DepInvFunc().doDepInvStuff(param1, param2, dep1, dep2);
   }

}
Dep1.java:

public class Dep1 {

   public int doDepStuff(String param1) {
      // do stuff
      return 5;
   }

}
Dep2.java:

public class Dep2 {

   public int doDepStuff(String param1) {
      // do stuff
      return 7;
   }

}

因此,这种方法(在使用动态类型语言时)的唯一缺点是,由于您可能间接调用函数,您(和/或您的IDE)可能无法检测到提供给这些间接函数调用的无效参数

当使用静态类型语言的编译时类型检查时,这个问题在很大程度上被克服了

这种方法避免了脆性和潜在的una