使用 Pex、Contracts 和 QuickGraph 进行误报单元测试
每个人。我在合约、pex 和 Quickgraph 之间有一个令人困惑的交互,非常感谢更有知识的人的建议。我将其归结为一个重现案例,其中注释掉一个合同会使误报消失,但我无法在允许的时间内使用调试器诊断它,因为主题代码(quickgraph)在属性方面有副作用 - getter,这意味着调试器在显示属性值时会执行副作用,从而干扰实际的执行顺序。
首先是一些背景知识,然后是具体细节,然后是一个可供下载和尝试的项目的指针,如果您愿意深入了解的话!
我安装了 Pex &鼹鼠
http://research.microsoft.com/en-us/projects/ pex/downloads.aspx
和 CodeContracts for .NET 4.0
http://research .microsoft.com/en-us/projects/contracts/
我通过 nuget 下载了最新版本的 QuickGraph,它都是为 .NET 3.5 构建的。我将其修剪到我需要的最低限度,进入所有项目属性,将它们从 .NET 3.5 Client Profile 全部更新到 .NET 4.0,修复了一个源代码破坏性更改(这是微不足道的,而且非常非常不可能与我的项目有任何联系)问题)。然后,我转到每个项目页面上的“代码合同”选项卡并启用所有静态和动态选项。
http://quickgraph.codeplex.com/releases/view/55262
该项目有 192单元测试,其中许多是 Pex 生成的(非常好!)。要运行测试,请从
http://dl.dropbox.com/ 获取我的项目 zip 文件u/1997638/QuickGraph.zip
确保您有 Pex 和 QuickGraph。来自上面链接的摩尔和合同。打开解决方案,重建所有内容,然后在解决方案级别“运行解决方案中的所有测试”(control-R、A)。一切都会过去的。然后转到 IImplicitUndirectedGraphContracts.cs 的第 49 行,取消注释大注释下的 Contract(由我插入)。一项测试,Prim12240WithDelegate 将失败。
该测试练习了一个图形构造函数,该构造函数通过在 Edges 和 EdgeCount 的属性获取器中调用用户提供的委托来动态构建边。可爱的。但是 IImplicitUndirecteGraphContracts.cs 第 49 行的合约出现了问题。
这是一个误报,因为如果我注释掉这个合同,测试就会通过。在尝试在调试器中遵循这一点时,它与属性 getter 中创建 Edge 的时间有关。然而,我无法解开这个问题,因为调试器调用这些 getter,主题代码调用它们,合约代码调用它们,也许是静态的,也许是动态的,我只是在尝试遵循它时迷失了方向,并认为我我会把这个问题提给那些比我更了解合同执行细节的人。
这是有问题的合同;注释掉它会使单元测试成功:
[Pure]
IEnumerable<TEdge> IImplicitUndirectedGraph<TVertex, TEdge>.AdjacentEdges(TVertex v)
{
IImplicitUndirectedGraph<TVertex, TEdge> ithis = this;
Contract.Requires(v != null);
Contract.Requires(ithis.ContainsVertex(v));
Contract.Ensures(Contract.Result<IEnumerable<TEdge>>() != null);
~~~~~~> Contract.Ensures(
Enumerable.All(
Contract.Result<IEnumerable<TEdge>>(),
edge =>
edge != null &&
ithis.ContainsEdge(edge.Source, edge.Target) &&
(edge.Source.Equals(v) || edge.Target.Equals(v))
)
);
return default(IEnumerable<TEdge>);
}
everyone. I have a perplexing interaction between contracts, pex, and quickgraph and would be very grateful for advice from the more knowledgeable. I have boiled it down to a repro case where commenting out one contract makes the false negative go away, but I have been unable to diagnose it with the debugger in the time allowed because the subject code (quickgraph) has side-effects in property-getters, meaning that the debugger executes the side effects when displaying the values of the properties, interfering with the actual order of execution.
First a little background, then the specifics, then a pointer to a project to download and try out, should you be so inclined as to dig in!
I installed Pex & Moles
http://research.microsoft.com/en-us/projects/pex/downloads.aspx
and CodeContracts for .NET 4.0
http://research.microsoft.com/en-us/projects/contracts/
I downloaded, via nuget, the most recent version of QuickGraph, which is all built for .NET 3.5. I pruned it to the minimum I needed, went into Project Properties for all, updated them all to .NET 4.0 from .NET 3.5 Client Profile, fixed one source breaking change (which was trivial and very, very unlikely to have any connection to my problem). I then went to the Code Contracts tab on every project page and enabled all static and dynamic options.
http://quickgraph.codeplex.com/releases/view/55262
The project has 192 unit tests, many of them Pex-generated (very nice!). To run the tests, get my project zip file from
http://dl.dropbox.com/u/1997638/QuickGraph.zip
Make sure you have Pex & Moles and Contracts from the links above. Open the solution, Rebuild everything, then, at the solution level, "Run All Tests In Solution" (control-R, A). All will pass. Then go to line 49 of IImplicitUndirectedGraphContracts.cs and uncomment the Contract under the large comment (inserted by me). One test, Prim12240WithDelegate will fail.
That test exercises a graph constructor that builds edges on-the-fly by calling a user-supplied delegate in the property-getters for Edges and EdgeCount. Cute. But something goes wrong with the Contract on line 49 of IImplicitUndirecteGraphContracts.cs.
This is a false negative, because if I comment out this contract, the test passes. In trying to follow this in the debugger, it has something to do with the timing of the creation of the Edges in the property getters. I haven't been able to disentangle this, however, because the debugger calls these getters, the subject code calls them, the contracts code calls them, maybe statically, maybe dynamically, I just plain got lost trying to follow it, and thought I'd bring the question up to those who understand the details of contract execution better than I.
Here is the offending contract; commenting it out makes the unit test succeed:
[Pure]
IEnumerable<TEdge> IImplicitUndirectedGraph<TVertex, TEdge>.AdjacentEdges(TVertex v)
{
IImplicitUndirectedGraph<TVertex, TEdge> ithis = this;
Contract.Requires(v != null);
Contract.Requires(ithis.ContainsVertex(v));
Contract.Ensures(Contract.Result<IEnumerable<TEdge>>() != null);
~~~~~~> Contract.Ensures(
Enumerable.All(
Contract.Result<IEnumerable<TEdge>>(),
edge =>
edge != null &&
ithis.ContainsEdge(edge.Source, edge.Target) &&
(edge.Source.Equals(v) || edge.Target.Equals(v))
)
);
return default(IEnumerable<TEdge>);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Pex 在处理 .NET 4.0 运行时中的 LINQ 表达式时存在问题。来自 此 MSDN 论坛帖子的第一个答案了解更多详情:
这可能会使其忽略您在评估过程中注释掉的合同,从而导致误报。
Pex has issues dealing with LINQ expressions in the .NET 4.0 runtime. from the first answer on this MSDN forum post for more details:
This could have potentially made it ignore the Contract that you have commented out during its evaluation, leading to the false negative.