我们有很多使用“Class.ReceivedCalls()”和“var tmp = Class.Received(int).Property;”的测试检查呼叫计数。 在 AutoFixture 的 v3 中,它正确报告了调用计数,尤其是属性,它不再这样做了。 方法的调用次数似乎还可以。
鉴于我们有以下代码:
public interface IRunSpeed
{
int Speed { get; }
}
public class GetToDaChoppa
{
private readonly IRunSpeed _runSpeed;
public GetToDaChoppa(IRunSpeed runSpeed)
{
_runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
}
public void DoItNow()
{
var runningSpeed = _runSpeed.Speed;
}
}
鉴于我们有以下测试:
[Fact]
public void DoItNow_WithOutAutoNSubstitute()
{
// Arrange
var runSpeed = Substitute.For<IRunSpeed>();
runSpeed.Speed.Returns(2);
var sut = new GetToDaChoppa(runSpeed);
//Act
sut.DoItNow();
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());
}
[Theory, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute(
[Frozen] IRunSpeed runSpeed,
GetToDaChoppa sut)
{
// Arrange
runSpeed.Speed.Returns(49);
//Act
sut.DoItNow();
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());
我的 AutoNSubstituteDataAttribute 看起来像这样:
public class AutoNSubstituteDataAttribute : AutoDataAttribute
{
public AutoNSubstituteDataAttribute()
: base(() => new Fixture()
.Customize(new AutoNSubstituteCustomization()))
{
}
}
第一个测试“DoItNow_WithOutAutoNSubstitute”工作正常。 但是第二个测试 'DoItNow_UsingAutoNSubstitute' 为 'ios.Received(1).Speed;' 返回 2。
它还为“runSpeed.ReceivedCalls();”返回 2。
因此,我们目前无法将我们的解决方案升级到 v4,因为我们每个解决方案立即有超过 1000 个失败的测试。 关于问题可能是什么或在哪里寻找修复的任何指导?
感谢您解决问题。 其实这个问题和AutoFixture没有关系,我们也受到了影响😕
此问题主要是由 xUnit 引起的,如果您按如下方式重写测试,它将通过:
```c#
[理论,AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute_CreateManually(IFixture fixture)
{
// 安排
var runSpeed = fixture.Freeze
var sut = fixture.Create
runSpeed.Speed.Returns(49);
//Act
sut.DoItNow();
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Single(runSpeed.ReceivedCalls());
}
```
当您运行测试时,xUnit 会尝试为您格式化测试名称。 如果它发现该类型未知并且没有ToString()
重载,它会使用结构检查并递归获取属性值。 如果您检查测试名称,您将看到以下内容:
您可能已经注意到,xUnit 获取了Speed
属性值以很好地向您展示测试参数。 由于这是通常的调用,NSubstitute 将其视为调用。
很奇怪,你在 v3 中没有这个问题,因为在这方面没有任何改变。 我刚刚测试了 xUnit2 + AutoFixture v3
并且仍然有问题。 可能您也升级了 xUnit。
至于解决方法,我有好消息和坏消息。 好消息是,这将在 NSubsitute 的下一个主要版本中得到解决,因为它开始覆盖ToString()
方法以返回代理 ID。 结果,xUnit 不再触及属性:
坏消息是 NSubsitute v4 还没有发布。
我可以建议你:
master
源代码,进行本地构建并使用您自己的NSubsitute.dll
而不是 NuGet 包。 v4 发布后,可以切换到 NuGet 包。对于给您带来的不便,我深表歉意,但不幸的是,我们无法在 AutoFixture 方面采取任何措施来解决此问题。
刚刚测试了这个。 最后,我们断言 ReceivedCalls 的测试又返回了一个成功的结果。
仍然失败的是 Received(x) 上的断言,如上面提到的 Received(1).Speed Daniel。
你也有帽子的答案/解决方案吗?
你们在做同一个项目吗? :) 如果是这样,你能澄清一下你是如何解决这个问题的吗?
上面建议的选项应该有助于解决这两个问题。 如果他们没有 - 澄清场景。
实际上不是同一个项目,而是在同一家公司。
我们一直在 xunit 1.9 + nsubstitute 2 / 3 上使用了很长时间,最终设法获得了我们的内部程序集(nuget 包,因此返回仅一个引用的旧版本的简单方法并不像听起来那么容易将)与 xunit2 兼容。 现在我们有这两个问题。
我们已经尝试了一些东西,最后认为它一定与 autofixture 有关 - 因为冻结属性和硬编码的测试执行计数 - 或类似的东西。
丹尼尔打开这个案例后,他把它的链接发给了我。
就像你上面描述的那样,我用新编译的 nsubstitute 项目中的参考替换了 nuget 3.1 参考。 在那之后,我现在有 118 个而不是 178 个失败的测试,因为 Receiced(x) 仍在评估错误的调用次数。
我的项目目前基于 .net 4.5.2 和 xunit2,引用了 nsubstitute 和 autofixture 4.2 的当前 repo 版本。 由于相同的两个原因,所有测试都失败了 - 在单元测试中仍然有一些接收到的呼叫失败。
我想当我再次回到办公室(复活节外出)时,我必须再次深入研究它。
也许@dklinger同时可以描述场景。
@dklinger @evilbaschdi请在有机会检查后跟进 - 很有趣,为什么即使在应用补丁后仍然会看到问题。
重新打开问题以表明我们仍在进行调查。
好的,所以我已经测试了过去几个小时。 首先:感谢您的回复、您的解释和建议 - 帮助很大。
我已经测试了 NSubstitute 的 v4。 它适用于我上面的示例代码,但不能完全解决我们现实世界项目中的问题。 问题是,它解决了错误的 ReceivedCalls-count 问题,仅适用于 SUT-Calls 方法,而不是属性。
我们现在工作的代码示例:
[Theory, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute(
[Frozen] IRunSpeed runSpeed,
GetToDaChoppa sut)
{
// Arrange
runSpeed.Speed.Returns(49);
//Act
sut.DoItNow();
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Equal(1, runSpeed.ReceivedCalls().Count());
}
但是如果我改变方法“GetToDaChoppa.DoItNow();” 要成为属性“GetToDaChoppa.DoItNow”,ReceivedCallsCount 再次为 +1:
[Theory, AutoNSubstituteData]
public void DoItNow_UsingAutoNSubstitute(
[Frozen] IRunSpeed runSpeed,
GetToDaChoppa sut)
{
// Arrange
runSpeed.Speed.Returns(49);
//Act
var x = sut.DoItNow;
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Equal(1, runSpeed.ReceivedCalls().Count());
}
我认为它再次与 xUnit 命名有关,因为我们为工作方法实现获得了这个命名:
还有一个用于非工作属性实现:
看起来 xUnit 正在调用 SUT-property 来检索其值以将其用于测试名称,但它并没有为 SUT-methods 执行此操作。 我的第一个想法是它与我们的方法“GetToDaChoppa()”的返回值有关,该值是无效的。 但即使将其更改为“public int GetToDaChoppa()”,xUnit 仍然没有调用它来获取测试名称的返回值。 这只是使用属性的问题。
现在我又被困住了。 我完全同意这不是 AutoFixture 的问题。 但在您看来,比我们更了解所有数据包,您有什么建议?
是的,当然。 这里是:
被测系统:
public interface IRunSpeed
{
int Speed { get; }
void Dude();
int Dude2();
}
public class GetToDaChoppa
{
private readonly IRunSpeed _runSpeed;
public GetToDaChoppa(IRunSpeed runSpeed)
{
_runSpeed = runSpeed ?? throw new ArgumentNullException(nameof(runSpeed));
}
public int DoItNow
{
get
{
var runningSpeed = _runSpeed.Speed;
return 0;
}
}
}
测试用例:
[Theory, AutoNSubstituteData]
public void DoItNowAsProperty_UsingAutoNSubstitute(
[Frozen] IRunSpeed runSpeed,
GetToDaChoppa sut)
{
// Arrange
runSpeed.Speed.Returns(49);
//Act
var x = sut.DoItNow;
//Assert
var tmp = runSpeed.Received(1).Speed;
Assert.Equal(1, runSpeed.ReceivedCalls().Count());
}
目前我正试图赶上https://github.com/xunit/xunit/issues/1386和https://github.com/AutoFixture/AutoFixture/issues/805上的讨论
我还尝试创建自己的 TheoryAttribute 覆盖 DisplayName 属性,但这也无济于事,因为 xUnit 在某些方面仍在内部使用自动生成的名称。 但这只是仅供参考,与上面的演示代码完全无关。
public sealed class MyTheoryAttribute : TheoryAttribute
{
public MyTheoryAttribute([CallerMemberName] string memberName = null)
{
DisplayName = "MyTestCase";
}
}
@dklinger感谢您提供详细的场景。 这确实非常棘手,很难以某种方式解决它。 从 AutoFixture 和 NSubsitute 的角度来看,代码是在 xUnit 内部深处还是在测试主体中调用没有区别。
通常,作为一种解决方法,您可以使用 NSubstitute 的明确功能:
runSpeed.ClearReceivedCalls();
此代码应在您验证确切调用次数的每个测试的序言中运行。 如果你有几个测试,它工作得很好,但是很明显,如果数千个测试受到影响,那将没有多大帮助😅
遗憾的是,NSubsitute + xUnit2 + AutoFixture 集成不能很好地工作并受到此类问题的困扰。 AutoFixture 产品旨在简化生活,而不是让它成为一场噩梦 😕 希望来自 xUnit 的人会建议您快速解决整个项目的问题。
如果您认为我们可以从我们这边做一些事情来改善这种情况,请告诉我。