Unit testing 使用依赖注入时,我是针对接口还是实现进行测试?
我有一个接口和实现类,在类的构造中使用依赖项注入:Unit testing 使用依赖注入时,我是针对接口还是实现进行测试?,unit-testing,dependency-injection,Unit Testing,Dependency Injection,我有一个接口和实现类,在类的构造中使用依赖项注入: class House { public: virtual void blah() = 0; //etc. all the interface of my class }; class HouseImpl : public House { protected: Door *front_door; public: HouseImpl(Door *door) : front_door(door) {} }; 现在,在我的
class House {
public:
virtual void blah() = 0; //etc. all the interface of my class
};
class HouseImpl : public House {
protected:
Door *front_door;
public:
HouseImpl(Door *door) : front_door(door) {}
};
现在,在我的工厂方法或任何实际构建类及其依赖项的方法中,我是传入依赖项还是动态创建它们
选项1
通过传入依赖项,我的单元测试可以使用相同的工厂传入模拟对象,并且我的测试只针对接口编写
House* get_house(Door* door) {
return new HouseImpl(door);
}
void unit_test_method_on_house() {
MockDoor mock_door;
House* class_under_test = get_house(mock_door);
}
在这种方法中,我认为工厂方法基本上是接口类型构造函数的抽象。我看到的缺点是,当我添加依赖项时,它们都必须添加到工厂方法签名中,这实际上只是将责任推给了客户机
选项2
另一种选择是工厂是生产代码中使用的,单元测试直接实例化实现类
House* get_house() {
return new HouseImpl(new Door()); //ignore the memmory management details here
}
void unit_test_method_on_house() {
MockDoor mock_door;
HouseImpl class_under_test(mock_door);
}
这里的问题是,我将单元测试扩展到依赖于实现类,而不仅仅依赖于接口。显然,我可以非常小心地编写好的测试,这些测试不会做出特定于实现的假设,但是在一个大型项目上,我不想假设所有开发人员都会像我一样小心
那么,这里推荐的方法是什么呢?当您进行单元测试时,您的单元测试本质上就是SUT。为了使单元测试能够识别和限制系统中的运动部件,这是必要的。换句话说,单元测试应该直接使用SUT类,以便将其与任何伪对象区分开来 另一种看待它的方式是,测试需要知道SUT的实际类型,以便知道要测试什么。接口关注“什么”,而实现关注“如何”。如果“如何”发生变化,则可能需要调整单元测试以考虑它 工厂对象的存在只是为了避免与创建的对象紧密耦合。由于单元测试并不担心与SUT紧密耦合,因此使用工厂并不会带来任何好处(它确实会成为阻碍)
编辑:你说得对,你仍然应该只测试SUT的公共接口。如果单元测试对SUT的内部工作有太多的了解,那么在不破坏测试的情况下重构代码就会变得更加困难 不过,单元测试与其SUT之间的紧密耦合不仅仅是一种负担;它可以对你有利。使用,您可以将某些方法公开给其他使用者不一定会看到的测试 SUT的构造函数属于此类别。例如,选项1中的factory方法假设返回的任何房屋都必须使用门,但选项2中的factory方法不使用门。存在
门
是工厂可能需要隐藏的实施细节(例如,房屋
的实施不需要门)
如果测试直接实例化SUT,您就不必担心这些类型的实现细节会泄漏到您的设计中。当您进行单元测试时,单元测试本身就是SUT的一部分。为了使单元测试能够识别和限制系统中的运动部件,这是必要的。换句话说,单元测试应该直接使用SUT类,以便将其与任何伪对象区分开来 另一种看待它的方式是,测试需要知道SUT的实际类型,以便知道要测试什么。接口关注“什么”,而实现关注“如何”。如果“如何”发生变化,则可能需要调整单元测试以考虑它 工厂对象的存在只是为了避免与创建的对象紧密耦合。由于单元测试并不担心与SUT紧密耦合,因此使用工厂并不会带来任何好处(它确实会成为阻碍)
编辑:你说得对,你仍然应该只测试SUT的公共接口。如果单元测试对SUT的内部工作有太多的了解,那么在不破坏测试的情况下重构代码就会变得更加困难 不过,单元测试与其SUT之间的紧密耦合不仅仅是一种负担;它可以对你有利。使用,您可以将某些方法公开给其他使用者不一定会看到的测试 SUT的构造函数属于此类别。例如,选项1中的factory方法假设返回的任何房屋都必须使用门,但选项2中的factory方法不使用门。存在
门
是工厂可能需要隐藏的实施细节(例如,房屋
的实施不需要门)
如果测试直接实例化SUT,您就不必担心这些类型的实现细节泄漏到您的设计中。您的单元测试可以依赖于实现,这样它就可以构造一个要测试的实例。这就是单元测试的目的:测试那个类。(选项2) 但是单元测试应该关注实现是否实现了它应该实现的接口,这是对的。您可以通过将测试实例声明为接口类型而不是实现类型来强调这一点 然而,有时您还需要测试那些需要自己测试的非常复杂的私有方法。在这种情况下,测试实例需要是实现类型
我发现第二种单元测试不太常见,所以当我写一种单元测试时,我只写一条注释,明确它是特定于实现的。您的单元测试可以依赖于实现,这样它就可以