单元测试:接口编码?
目前我的项目由各种具体类组成。现在,当我进入单元测试时,看起来我应该为每个类创建一个接口(实际上使我的项目中的类数量增加了一倍)?我碰巧使用 Google Mock 作为模拟框架。请参阅界面上的 Google Mock CookBook。以前我可能只有类 Car
和 Engine
,现在我会有抽象类(又名 C++ 接口)Car
和 Engine< /code> 然后是实现类
CarImplementation
和 EngineImpl
或其他类。这将允许我消除 Car
对 Engine
的依赖。
我在研究这一问题时遇到了两种思路:
仅当您可能需要多个接口时才使用接口 给定抽象的实现和/或在公共 API 中使用, 因此,否则不要创建不必要的接口。
单元测试存根/模拟 通常是“其他实现”,所以,是的,您应该创建 接口。
在进行单元测试时,我应该为项目中的每个类创建一个接口吗? (我倾向于创建接口以方便测试)
Currently my project is composed of various concrete classes. Now as I'm getting into unit testing it looks like I'm supposed to create an interface for each and every class (effectively doubling the number of classes in my project)? I happen to be using Google Mock as a mocking framework. See Google Mock CookBook on Interfaces. While before I might have just classes Car
and Engine
, now I would have abstract classes (aka C++ interfaces) Car
and Engine
and then the implementation classes CarImplementation
and EngineImpl
or whatever. This would allow me to stub out Car
's dependency on Engine
.
There are two lines of thought I have come across in researching this:
Only use interfaces when you may have the need for more than one
implementation of a given abstraction and/or for use in public APIs,
so otherwise don't create interfaces unnecessarily.Unit tests stubs/mocks
often are the "other implementation", and so, yes, you should create
intefaces.
When unit testing, should I create an interface for each class in my project? (I'm leaning towards creating interfaces for ease of testing)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
认为你有很多选择。正如您所说,一种选择是创建接口。假设您有类
要引入接口 - 您必须更改 Car 以采用 Engine
如果您只有一种类型的引擎 - 您突然使您的 Car 对象更难使用(谁创建引擎,谁拥有引擎)。汽车有很多零件——所以这个问题会不断增加。
如果您想要单独的实现,另一种方法是使用模板。这消除了对接口的需要。
在您的模拟中,您可以使用专用引擎创建汽车:
另一种不同的方法是向 Engine 添加方法以允许对其进行测试,例如:
然后您可以向 Car 添加检查方法,或者从 Car 继承进行测试。
这需要将 Car 类中的 Engine 从私有更改为受保护。
根据现实世界的情况,将取决于哪种解决方案是最好的。此外,每个开发人员对于如何对代码进行单元测试都有自己的圣杯。我个人的观点是牢记客户/顾客。让我们假设您的客户(也许是您团队中的其他开发人员)将创建汽车并且不关心引擎。因此,我不想公开引擎(我的库内部的类)的概念,这样我就可以对它进行单元测试。我会选择不创建接口并一起测试两个类(我给出的第三个选项)。
Think you've got a number of options. As you say, one option is to create interfaces. Say you have classes
To introduce interfaces - you would have to change Car to take an Engine
If you've only got one type of engine - you've suddenly made your Car objects harder to use (who creates the engines, who owns the engines). Cars have a lot of parts - so this problem will continue to increase.
If you want seperate implementations, another way would be with templates. This removes the need for interfaces.
In your mocks, you could then create Cars with specialised engines:
Another, different approach, would be to add methods to Engine to allow it to be tested, something like:
You could then either add a check method to Car, or inherit from Car to test.
This would require Engine to be changed from private to protected in the Car class.
Depending on the real world situation, will depend on which solution is best. Also, each developer will have their own holy grail of how they believe code should be unit tested. My personal views is to keep the client/customer in mind. Lets assume your clients (perhaps other developers in your team) will be creating Cars and don't care about Engines. I would therefore not want to expose the concepts of Engines (a class internal to my library) just so I can unit test the thing. I would opt for not creating interfaces and testing the two classes together (third option I gave).
关于实现可见性的测试有两类:黑盒测试和白盒测试
黑盒测试侧重于通过接口测试实现,并验证对其规范的调整。
白盒测试测试有关实施的具体细节,通常不应该可以从外部访问这些细节。此类测试将验证实施组件是否按预期工作。 试图找出问题所在或需要维护的开发人员来说最感兴趣。
根据其定义适合模块化架构的维护模拟的开发人员来说最感兴趣,但这并不意味着项目中的所有类都需要完全模块化。当一组类彼此了解时,划清界限是完全可以的。它们作为一个组可以从某个外观接口类的角度呈现给其他模块。但是,您仍然希望在该模块中拥有白盒测试驱动程序,并了解实现细节。因此,这种测试不太适合模拟。
由此可见,您不需要为所有事情提供模拟或接口。只需采用实现外观接口的高级设计组件并为它们创建模拟即可。它会给你一个最佳点,模拟测试会带来回报恕我直言
话虽如此,尝试使用该工具来满足你的需求,而不是让该工具强迫你进行你认为对实际情况无益的更改。从长远来看
there are two categories of testing regarding implementation visibility: black-box testing and white-box testing
black-box testing focuses on testing implementation through their interfaces, and validating the adjust to their spec.
white-box testing tests granular details about the implementation that SHOULD NOT in general be accessible from the outside. This sort of testing will validate that the implementation components work as intended. So their results are mostly of interest to developers trying to figure out what is broken, or needs mantainance
mocks by their definition fit into modular architectures, but it doesn't follow that all classes in a project need to be entirely modular out themselves. Its perfectly fine to draw some line when a group of classes will know about each other. They as a group can present to other modules from the persepective of some facade interface class. However, you'll still want to have white-box test drivers inside this module with knowledge about the implementation details. Hence this sort of testing is not a good fit for mocks.
It follows trivially from this that you don't need to have mocks or interfaces for everything. Just take the high-level design components that implement facade interfaces and create mocks for them. It will give you the sweet spot where mock testing pays off IMHO
having said that, try to use the tool to your needs, rather than letting the tool force you into changes you think will not be beneficial in the long run
为项目中的每个类创建接口可能有必要,也可能没有必要。这完全是一个设计决定。我发现大部分情况并非如此。通常,在n 层设计中,您希望抽象数据访问和逻辑之间的层。我认为您应该朝这个方向努力,因为它有助于测试逻辑,而无需太多测试所需的基础设施。像 依赖注入和 IoC 这样的抽象方法需要你做这样的事情,并且会让它变得更容易测试所说的逻辑。
我会检查您正在尝试测试的内容,并重点关注您认为最容易出错的领域。这可以帮助您决定接口是否必要。
Creating interfaces for every class within your project may or may not be necessary. This is entirely a design decision. I've found that it mostly isn't. Often in n-tier design you wish you abstract the layer between data access and logic. I would argue that you should work towards this as it aids in testing the logic without much infrastructure necessary for the tests. Methods of abstraction like dependency injection and IoC would require you to do something like this and would make it easier to test said logic.
I would examine what you are trying to test and focus on the areas that you view as most prone to error. This can help you decide whether interfaces are necessary.