这是一个困难的,开放式的问题,我知道,但我想我扔在地上,看看如果任何人有任何有趣的建议。
我已经开发出一种码生成器,其将我们Python接口我们的C ++代码(通过SWIG生成),并产生以暴露此作为Web服务所需的代码。 当我开发这个代码,我做到了使用TDD,但是我发现我的测试是脆弱得要命。 因为每个测试基本上是想验证的输入给定码位(这恰好是一个C ++头)我得到的输出给定码位我写了一个小引擎,从XML输入文件读取测试定义和生成测试从这些预期的情况下。
问题是我害怕去到所有修改代码。 这和单元测试本身的事实是:复杂的,和b:脆。
所以我试图想其他办法解决这个问题,这让我觉得我也许解决了错误的方式。 也许我需要更多地关注结果,即:不代码生成我实际运行,做什么,我想让它,而不是,没有代码看起来我希望它的方式。
有没有人得到了类似这样的东西,他们会愿意分享任何经验?
我开始写了我有我自己的代码发生器经验的总结,然后回去和重新阅读你的问题,发现你已经在同样的问题触动自己,专注于执行结果,而不是代码布局/看。
问题是,这是很难测试,生成的代码可能不适合实际的单元测试系统的环境中运行,你怎么编码预期的结果?
我发现,您需要将代码生成分解成小块和单元测试的。 单元测试全码发生器更像单元测试集成测试,如果你问我。
回想一下,“单元测试”只是一种测试。 你应该能够单元测试你的代码生成的堆内构件。 你真正看这里是系统级测试(又名回归测试)。 它不只是语义...有不同的思维方式,方法,期望等这当然更多的工作,但你可能需要咬紧牙关,建立一个终端到终端的回归测试套件:固定的C ++文件 - >痛饮接口 - > python模块 - >已知的输出。 你真的要检查与预期输出的已知输入(固定的C ++代码)(什么出来最终Python程序)。 检查代码生成结果直接会像版本比较的对象文件...
是的,结果是唯一重要的事情。 真正的苦差事是写一个框架,允许您生成的代码独立运行...花你的时间在那里。
如果您正在运行*马钱,你可能会考虑,取而代之的是bash脚本或生成文件的倾销单元测试框架。 在Windows上,你可能会考虑建立一个shell应用/功能运行的发电机,然后使用代码(如另一个进程)和单元测试这一点。
第三个选择是将生成的代码,然后建立由它的应用程序,包括什么,但一个单元测试。 同样的,你就需要一个shell脚本或诸如此类的东西为每路输入运行此。 至于如何编码预期的行为,它发生,我认为它可以在几乎相同的方式只是用生成的接口,而不是C ++的一个来完成,你会为C ++代码。
只是想指出的是,你仍然可以实现细粒度的测试,同时验证结果:您可以通过嵌套他们内部的一些设置和验证码测试的代码块个人:
int x = 0;
GENERATED_CODE
assert(x == 100);
只要你有从更小的块组装生成的代码,而块不经常改变,你可以锻炼更多的条件和测试好一点,并希望避免当你改变一个块的细节所有的测试打破。
单元测试仅仅是测试一个特定的单元。 所以,如果你正在写的A类规范,它如果A类没有B类和C的实际具体版本是理想
好吧,我注意到后来对这一问题的标签包括C ++ / Python,但原理是一样的:
public class A : InterfaceA
{
InterfaceB b;
InterfaceC c;
public A(InterfaceB b, InterfaceC c) {
this._b = b;
this._c = c; }
public string SomeOperation(string input)
{
return this._b.SomeOtherOperation(input)
+ this._c.EvenAnotherOperation(input);
}
}
由于上述系统A至喷射系统B和C的接口,就可以进行单元测试只是系统A,而无需真正的功能正在由任何其他系统来执行。 这是单元测试。
下面是从创建接近系统完成,具有不同规格时,每件行为的一个巧妙的方式:
public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
private string _actualString;
private string _expectedString;
private string _input;
private string _returnB;
private string _returnC;
[It]
public void Should_return_the_expected_string()
{
_actualString.Should().Be.EqualTo(this._expectedString);
}
public override void GivenThat()
{
var randomGenerator = new RandomGenerator();
this._input = randomGenerator.Generate<string>();
this._returnB = randomGenerator.Generate<string>();
this._returnC = randomGenerator.Generate<string>();
Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
.Return(this._returnB);
Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
.Return(this._returnC);
this._expectedString = this._returnB + this._returnC;
}
public override void WhenIRun()
{
this._actualString = Sut.SomeOperation(this._input);
}
}
所以在最后,一个单元/规格可以有多个行为,并为您开发的单元/系统规范的增长; 如果您在测试系统依赖于其他具体制度内的,要小心。
我的建议是找出一组已知的输入输出结果,比如一些你已经到位简单的情况,并进行单元测试所产生的代码 。 这是完全可能的,因为你改变了发电机所产生的确切字符串可能会稍有不同......但你真正关心的是它是否以同样的方式解释。 因此,如果测试结果就好像它是你的特点,你会测试代码,你会发现,如果它在你想要的方式成功。
基本上,你真的想知道什么是你的发电机是否会产生你所期望的无需物理测试每一个可能的组合(又作:不可能的)。 确保您的发电机是你所期望的方式一致,因此可以感觉更好,发电机将在不断更复杂的情况下取得成功。
这样一来,你也可以建立一套回归测试(需要保持正常工作的单元测试)。 这将帮助您确保更改您的发电机没有违反其他形式的代码。 当你遇到你的单元测试没有赶上一个错误,你可能要包括,以防止类似的破损。
我发现,你需要测试你产生什么比你如何产生更多。
在我的情况下,程序生成多种类型的代码(C#,HTML,SCSS,JS,等),其编译成一个Web应用程序。 我发现,以减少回归bug,最好的办法就是全面测试Web应用程序本身,而不是通过测试发电机。
不要误会我的意思,还有单元测试检查出一些生成器代码,但我们最大的为我们的划算已经上生成应用程序本身的UI测试。
由于我们产生它我们还产生一个很好的抽象的JS,我们可以用它来编程测试应用程序。 其次,我们在这里列出了一些想法: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089
伟大的部分是,它真的很考验你的系统终端到终端,从代码生成出来你实际上产生。 一旦测试失败,它很容易跟踪它回发生器打破了这里。
这是非常甜蜜。
祝好运!