Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.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_Mocking - Fatal编程技术网

Unit testing 遗留代码的单元测试,对象创建

Unit testing 遗留代码的单元测试,对象创建,unit-testing,mocking,Unit Testing,Mocking,我正在向现有应用程序添加单元测试。类关系如下所示: class TopClass creates class A and B and common class AB; class A creates class A1, A2, A3, A4 and AB; class B creates class B1 and AB; public class EntryPoint() { AB ab = new AB(); Provider<TopClass> provideTopC

我正在向现有应用程序添加单元测试。类关系如下所示:

class TopClass creates class A and B and common class AB;
class A creates class A1, A2, A3, A4 and AB;
class B creates class B1 and AB;
public class EntryPoint() {
  AB ab = new AB();

  Provider<TopClass> provideTopClass() {
    return new Provider<TopClass>() {
      @Override public TopClass get() {
        return new TopClass(provideA().get(), provideB().get(), provideAB().get());
      }
    };
  }

  Provider<A> provideA() {
    return new Provider<AB>() {
      @Override public AB get() {
        return new A(provideA1().get(), provideA2.get(), ...);
      }
    };
  }

  Provider<AB> provideAB() {
    return new Provider<AB>() {
      @Override public AB get() {
        return ab;   // always reuse the same instance if you'd like
      }
    };
  }

  // and so forth
}
我被告知在创建顶级对象时传递所有依赖对象(作为接口),以便进行依赖项注入。因此,需要更改类构造函数,如下所示:

TopClass(A a, A1 a1, A2 a2, A3 a3, A4 a4, AB ab, B b, B1 b1)
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab)
B(B1 b1, AB ab)

这是正确的/好的方法吗?是否有一种方法不需要在应用程序开始时创建所有对象

不,那不是个好办法

如果可能,您只需传入接口A和接口B即可

TopClass(A a, B b)
这样,
TopClass
的单元测试只关心它的直接依赖关系,而不关心依赖关系的依赖关系。这还将允许您在测试中模拟
A
B
的实现


然后,对于
A
的测试,您可能有一个通过A1、A2等的构造函数,但
TopClass
不应该知道这一点。

如果您按照以下顺序创建实例:

AB ab = new AB();//and so on for all classes wihtout dependencies like a1, a2, ..., b1
A a = new A(a1, a2, a3, a4, ab)
B b = new B(b1, ab)
TopClass(a, b, ab)
但正如您所说,
TopClass
创建了这些类的实例,这才是真正的问题。它应该期望实例,而不是创建它们。 也许这样,它甚至不需要ab参数,如果它只用于创建A和B

使用一些依赖注入框架/库可能是从开发人员那里拿走大量样板文件的一种方法,但其成本如下

  • 更改代码以使其在应用程序和测试中正常工作
  • 运行时性能(取决于框架)

应该考虑。

简短回答:是的,这就是你的起点,但不一定是一个好的终点。TopClass应该只关心
A
B
AB
的(接口)。这些对象的实现和依赖关系与TopClass无关。这也意味着模拟TopClass应该非常简单:您需要一个模拟a、一个模拟B和一个模拟AB,您可以直接将它们传入

如何创建
A
,而不考虑
A
的依赖关系?接收
提供程序不是
实例,而是Java内置的无参数工厂接口

此时,您的构造函数如下所示:

TopClass(A a, B b, AB ab)
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab)
B(B1 b1, AB ab)
如果您想延迟创建直到需要它,或者如果您需要多个提供程序,则可以使用
提供程序替换这些类型中的任何一个


“等等!”我听见你哭了。“这难道不意味着我必须创建一系列复杂的样板提供程序,以便对TopClass隐藏实现/依赖项细节吗?”是的,确实如此,您可以手动完成这项工作——或者您可以使用依赖项注入框架,如Spring、Guice或Dagger

手动模式看起来像这样:

class TopClass creates class A and B and common class AB;
class A creates class A1, A2, A3, A4 and AB;
class B creates class B1 and AB;
public class EntryPoint() {
  AB ab = new AB();

  Provider<TopClass> provideTopClass() {
    return new Provider<TopClass>() {
      @Override public TopClass get() {
        return new TopClass(provideA().get(), provideB().get(), provideAB().get());
      }
    };
  }

  Provider<A> provideA() {
    return new Provider<AB>() {
      @Override public AB get() {
        return new A(provideA1().get(), provideA2.get(), ...);
      }
    };
  }

  Provider<AB> provideAB() {
    return new Provider<AB>() {
      @Override public AB get() {
        return ab;   // always reuse the same instance if you'd like
      }
    };
  }

  // and so forth
}
public类入口点(){
AB=新的AB();
提供程序provideTopClass(){
返回新的提供程序(){
@重写公共TopClass get(){
返回新的TopClass(provideA().get(),provideB().get(),provideAB().get());
}
};
}
提供者provideA(){
返回新的提供程序(){
@重写公共AB get(){
返回新的A(provide1().get(),provide2.get(),…);
}
};
}
提供者provideb(){
返回新的提供程序(){
@重写公共AB get(){
return ab;//如果愿意,请始终重用同一实例
}
};
}
//诸如此类
}

在这里,您刚刚将所有的创建和连接提取到一个类中,Guice可以在运行时模拟该类,Dagger可以在编译时直接生成该类。您需要与您的团队合作来选择所需的框架,或者手动启动,但总体而言,这将为创建一个易于组装和易于测试的应用程序提供很多好处。

一个小提示:不要只是机械地颠倒内部使用的每个类的依赖关系。有一个区别称为:如果某个对象是对等对象,则可以请求依赖关系,例如通过构造函数注入。如果一个对象是一个“内部”实现细节,没有使用任何使代码难以测试的东西,那么您可能会认为它不值得注入,只需继续在TopClass本身中创建它。关于样板提供程序,使用提供程序类将所有对象创建放在一个类中的目的是什么?还有其他我不知道的好处吗?目的不是将对象创建放在一个类中,目的是将其与包含实际业务逻辑的类分开。它实际上可以实现为一个或多个类,也可以手写或自动生成,但任何DI/IoC解决方案(包括上面的dkatzel解决方案)都需要它,以避免像您这样的显式构造函数。