如何为第三方遗留代码创建测试对象
我有一个代码库,其中我实现的许多类都派生自公司其他部门提供的类。 与这些其他部门合作通常具有工作关系,就好像他们是第三方中间件供应商一样。
我正在尝试编写测试代码而不修改这些基类。 然而,创建有意义的测试存在问题 由于缺乏接口而导致对象:
//ACommonClass.h
#include "globalthermonuclearwar.h" //which contains deep #include dependencies...
#include "tictactoe.h" //...and need to exist at compile time to get into test...
class Something //which may or may not inherit from another class similar to this...
{
public:
virtual void fxn1(void); //which often calls into many other classes, similar to this
//...
int data1; //will be the only thing I can test against, but is often meaningless without fxn1 implemented
//...
};
我通常会提取一个接口并从那里工作,但由于这些是“第三方”,我无法提交这些更改。
目前,我已经创建了一个单独的文件,其中包含第三方提供的基类标头中定义的函数的虚假实现,这些函数是根据需要了解的,如“使用遗留代码”一书中所述。
我的计划是继续使用这些定义并为我需要的每个第三方类提供替代测试实现:
//SomethingRequiredImplementations.cpp
#include "ACommonClass.h"
void CGlobalThermoNuclearWar::Simulate(void) {}; // fake this and all other required functions...
// fake implementations for otherwise undefined functions in globalthermonuclearwar.h's #include files...
void Something::fxn1(void) { data1 = blah(); } //test specific functionality.
但在我开始这样做之前,我想知道是否有人尝试在与我类似的代码库上提供实际对象,这将允许创建新的测试特定类来代替实际的第三方类。
请注意,所有相关代码库都是用 C++ 编写的。
I have a code base where many of the classes I implement derive from classes that are provided by other divisions of my company. Working with these other devisions often have the working relationship as though they are third party middle ware vendors.
I'm trying to write test code without modifying these base classes. However, there are issues with creating meaningful test
objects due to the lack of interfaces:
//ACommonClass.h
#include "globalthermonuclearwar.h" //which contains deep #include dependencies...
#include "tictactoe.h" //...and need to exist at compile time to get into test...
class Something //which may or may not inherit from another class similar to this...
{
public:
virtual void fxn1(void); //which often calls into many other classes, similar to this
//...
int data1; //will be the only thing I can test against, but is often meaningless without fxn1 implemented
//...
};
I'd normally extract an interface and work from there, but as these are "Third Party", I can't commit these changes.
Currently, I've created a separate file that holds fake implementations for functions that are defined in the third-party supplied base class headers on a need to know basis, as has been described in the book "Working with Legacy Code".
My plan was to continue to use these definitions and provide alternative test implementations for each third party class that I needed:
//SomethingRequiredImplementations.cpp
#include "ACommonClass.h"
void CGlobalThermoNuclearWar::Simulate(void) {}; // fake this and all other required functions...
// fake implementations for otherwise undefined functions in globalthermonuclearwar.h's #include files...
void Something::fxn1(void) { data1 = blah(); } //test specific functionality.
But before I start doing that I was wondering if any one has tried providing actual objects on a code base similar to mine, which would allow creating new test specific classes to use in place of actual third-party classes.
Note all code bases in question are written in C++.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
模拟对象适合此类任务。 它们允许您模拟其他组件的存在,而无需它们存在。 您只需在测试中定义预期的输入和输出。
Google 有一个很好的 C++ 模拟框架。
Mock objects are suitable for this kind of task. They allow you to simulate the existence of other components without needing them to be present. You simply define the expected input and output in your tests.
Google have a good mocking framework for C++.
我现在遇到了一个非常类似的问题。 我不想添加一堆仅用于测试目的的接口,因此我无法使用任何现有的模拟对象库。 为了解决这个问题,我做了同样的事情,创建一个具有虚假实现的不同文件,并让我的测试链接虚假行为,而生产代码链接真实行为。
我希望此时能做的是获取另一个模拟框架的内部结构,并在我的假对象中使用它。 看起来有点像这样:
Production.h
Mock.h
ProductionCode.cpp
Test.cpp
它 这一切中最痛苦的部分是其他模拟框架与此非常接近,但没有完全做到这一点,并且宏是如此复杂,以至于适应它们并不简单。 我已经开始在业余时间研究这个问题,但进展并不很快。 即使我让我的方法按照我想要的方式工作,并且已经设置了期望设置代码,这种方法仍然有一些缺点,其中之一是,如果您必须链接到,您的构建命令可能会有点长很多 .o 文件而不是一个 .a,但这是可以管理的。 也不可能落入默认实现,因为我们没有链接它。 不管怎样,我知道这并不能回答问题,或者甚至不能告诉你任何你不知道的事情,但它显示了 C++ 社区距离模拟没有纯虚拟接口的类有多近。
I'm running into a very similar problem at the moment. I don't want to add a bunch of interfaces that are only there for the purpose of testing, so I can't use any of the existing mock object libraries. To get around this I do the same thing, creating a different file with fake implementations, and having my tests link the fake behaviour, and production code links the real behaviour.
What I wish I could do at this point, is take the internals of another mock framework, and use it inside my fake objects. It would look a little something like this:
Production.h
Mock.h
ProductionCode.cpp
Test.cpp
The most painful part of all this is that the other mock frameworks are so close to this, but don't do it exactly, and the macros are so convoluted that it's not trivial to adapt them. I've begun looking into this on my spare time, but it's not moving along very quickly. Even if I got my method working the way I want, and had the expectation setting code in place, this method still has a couple drawbacks, one of them being that your build commands can get to be kind of long if you have to link against a lot of .o files rather than one .a, but that's manageable. It's also impossible to fall through to the default implementation, since we're not linking it. Anyway, I know this doesn't answer the question, or really even tell you anything you don't already know, but it shows how close the C++ community is to being able to mock classes that don't have a pure virtual interface.
您可能需要考虑mocking而不是伪装作为潜在的解决方案。 在某些情况下,如果原始类不是可模拟的,您可能需要编写可模拟的包装类。 我已经使用 C#/.Net 中的框架类完成了此操作,但没有使用 C++,所以 YMMV。
You might want to consider mocking instead of faking as a potential solution. In some cases you may need to write wrapper classes that are mockable if the original classes aren't. I've done this with framework classes in C#/.Net, but not C++ so YMMV.
如果我有一个需要测试的类,该类派生自我无法(或不想)在测试下运行的类,我将:
如果我有一个只需要在测试中使用的类,但使用真实的类是一个问题(依赖项或不需要的行为):
例如,我像这样包装MFC的GDI类来测试Windows GDI绘图代码。 模板可以使这一切变得更容易 - 但我们经常由于各种技术原因而最终不这样做(Windows DLL 类导出的东西......)。
我确信所有这些都在 Feather 的《Working with Legacy Code》一书中 - 而且我所描述的内容都有实际的术语。 只是别让我把书从书架上拿下来......
If I have a class that I need under test that derives from something I can't (or don't want to) run under test I'll:
If I have a class that I just need to use in testing, but using the real class is a problem (dependencies or unwanted behaviors):
For example, I wrap MFC's GDI classes like this to test Windows GDI drawing code. Templates can make some of this easier - but we often end up not doing that for various technical reasons (stuff with Windows DLL class exporting...).
I'm sure all this is in Feather's Working with Legacy Code book - and what I'm describing has actual terms. Just don't make me pull the book off the shelf...
您在问题中没有指出的一件事是您的类派生自其他部门的基类的原因。 这种关系真的是一种 IS-A 关系吗?
除非您的类需要由框架使用,否则您可以考虑支持委托而不是继承。 然后,您可以使用依赖项注入在单元测试中为您的类提供其类的模拟。
否则,一个想法是编写一个脚本来从它们提供的标头中提取和创建您需要的接口,并将其集成到编译过程中,以便您的单元测试可以签入。
One thing you did not indicate in your question is the reason why your classes derive from base classes from the other division. Is the relationship really a IS-A relationshiop ?
Unless your classes needs to be used by a framework, you could consider favoring delegation over inheritance. Then you can use dependency injection to provide your class with a mock of their class in the unit tests.
Otherwise, an idea would be to write a script to extract and create the interface your need from the header they provide, and integrate this to the compilation process so your unit test can ve checked in.