c# – AutoFixtureAutoMoq忽略注入的实例冻结模拟

c# – AutoFixtureAutoMoq忽略注入的实例冻结模拟,第1张

概述现在解决方案已经找到了短的外卖: AutoFixture返回冻结模拟就好了;我自己也是通过AutoFixture生成的,它的公共属性具有局部默认值,对于测试非常重要,AutoFixture设置为一个新的值.除了马克的答案之外,还有很多东西要学习. 原来的问题: 我昨天开始尝试AutoFixture,我的xUnit.net测试有Moq全部.我希望替换一些Moq的东西,或者使它更容易阅读,而且我特别感 现在解决方案已经找到了短的外卖:

autoFixture返回冻结模拟就好了;我自己也是通过autoFixture生成的,它的公共属性具有局部默认值,对于测试非常重要,autoFixture设置为一个新的值.除了马克的答案之外,还有很多东西要学习.

原来的问题:

我昨天开始尝试autoFixture,我的xUnit.net测试有Moq全部.我希望替换一些Moq的东西,或者使它更容易阅读,而且我特别感兴趣的是在SUT Factory中使用autoFixture.

我用几个马克·塞曼在autoMocking上的博文发表了自己的抱怨,并尝试从那里开始工作,但是我没有得到很大的收获.

这是我的测试看起来没有autoFixture:

[Fact]public voID GetXml_ReturnsCorrectXElement(){    // Arrange    string xmlString = @"        <mapPings>            <mapPing source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />            <mapPing source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />        </mapPings>";    string settingKey = "gcCreditApplicationUsdFIEldMapPings";    Mock<ISettings> settingsMock = new Mock<ISettings>();    settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);    ISettings settings = settingsMock.Object;    ITracingService tracing = new Mock<ITracingService>().Object;    XElement expectedXml = XElement.Parse(xmlString);    IMapPingXml sut = new SettingMapPingXml(settings,tracing);    // Act    XElement actualXml = sut.GetXml();    // Assert    Assert.True(XNode.deepequals(expectedXml,actualXml));}

这里的故事很简单 – 确保SettingMapPingXml使用正确的键(硬编码/属性注入)查询ISettings依赖关系,并将结果作为XElement返回.仅当出现错误时,ITracingService才是相关的.

我想要做的是摆脱明确创建ITracingService对象,然后手动注入依赖关系的需要(不是因为这个测试太复杂,而是因为它足够简单,可以尝试和理解它们).

输入autoFixture – 第一次尝试:

[Fact]public voID GetXml_ReturnsCorrectXElement(){    // Arrange    IFixture fixture = new Fixture();    fixture.Customize(new automoqCustomization());    string xmlString = @"        <mapPings>            <mapPing source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />            <mapPing source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />        </mapPings>";    string settingKey = "gcCreditApplicationUsdFIEldMapPings";    Mock<ISettings> settingsMock = new Mock<ISettings>();    settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);    ISettings settings = settingsMock.Object;    fixture.Inject(settings);    XElement expectedXml = XElement.Parse(xmlString);    IMapPingXml sut = fixture.CreateAnonymous<SettingMapPingXml>();    // Act    XElement actualXml = sut.GetXml();    // Assert    Assert.True(XNode.deepequals(expectedXml,actualXml));}

检测到ISettings构造函数参数后,我会期望CreateAnonymous< SettingMapPingXml>()注意到已经为该接口注册了一个具体实例并注入了该实例 – 但是它不会这样做,而是创建一个新的匿名实现.

这是特别令人困惑的fixture.CreateAnonymous< ISettings>()确实返回我的实例 –

IMapPingXml sut = new SettingMapPingXml(fixture.CreateAnonymous<ISettings>(),fixture.CreateAnonymous<ITracingService>());

使测试完美的绿色,这一行正是我预期autoFixture在实例化SettingMapPingXml时内部做的.

那么就有一个冻结一个组件的概念,所以我只是在夹具中冻结模拟,而不是得到嘲弄的对象:

fixture.Freeze<Mock<ISettings>>(f => f.Do(m => m.Setup(s => s.Get(settingKey)).Returns(xmlString)));

只要我明确地调用SettingMapPingXml构造函数,并且不依赖于CreateAnonymous(),那么这个工作完全正常.

简单地说,我不明白为什么它的工作方式显然是这样,因为它违背了我可以想到的任何逻辑.
通常我会怀疑图书馆的错误,但这是一件非常基本的事情,我相信别人会遇到这种情况,很久以来就被发现和修复了.更重要的是,了解Mark对测试和DI的毅力,这不是无意的.

这反过来意味着我一定会错过一些比较小的东西.如何使用autoFixture创建的SUT与预配置的嘲弄对象作为依赖关系?我现在唯一确定的是,我需要automoqCustomization,所以我不必为ITracingService配置任何东西.

autoFixture / automoq包是2.14.1,Moq是3.1.416.3,全部来自NuGet. .NET版本是4.5(与VS2012一起安装),VS2012和2010中的行为是一样的.

在写这篇文章时,我发现有些人在使用Moq 4.0和装配绑定重定向时遇到问题,所以我仔细地清理了我对Moq 4的任何实例的解决方案,并通过将autoFixture.automoq安装到“clean”项目中,安装了Moq 3.1.但是,我的测试行为保持不变.

感谢任何指针和解释.

更新:这里是马克要求的构造函数代码:

public SettingMapPingXml(ISettings settingSource,ITracingService tracing){    this._settingSource = settingSource;    this._tracing = tracing;    this.SettingKey = "gcCreditApplicationUsdFIEldMapPings";}

为了完整性,GetXml()方法如下所示:

public XElement GetXml(){    int errorCode = 10600;    try    {        string mapPingSetting = this._settingSource.Get(this.SettingKey);        errorCode++;        XElement mapPingXml = XElement.Parse(mapPingSetting);        errorCode++;        return mapPingXml;    }    catch (Exception e)    {        this._tracing.Trace(errorCode,e.Message);        throw;    }}

SettingKey只是一个自动属性.

解决方法 假设SettingKey属性定义如下,我现在可以重现问题:
public string SettingKey { get; set; }

发生的情况是,注入到SettingMapPingXml实例中的Test Doubles是完全正确的,但是由于SettingKey是可写的,autoFixture的自动属性功能会启动并修改该值.

考虑这个代码:

var fixture = new Fixture().Customize(new automoqCustomization());var sut = fixture.CreateAnonymous<SettingMapPingXml>();Console.Writeline(sut.SettingKey);

这样打印出这样的东西:

SettingKey83b75965-2886-4308-bcc4-eb0f8e63de09

即使所有的测试双打都被适当地注入,但是没有满足安装方法的期望.

有很多方法可以解决这个问题.

保护不变量

解决此问题的正确方法是使用单元测试和autoFixture作为反馈机制.这是GOOS的要点之一:单元测试的问题通常是设计缺陷的症状,而不是单元测试(或autoFixture)本身的故障.

在这种情况下,它向我指出the design isn’t fool-proof enough.客户端可以随意 *** 纵SettingKey是否真的适合?

作为一个最低限度,我会推荐一个这样的替代实现:

public string SettingKey { get; private set; }

随着这种变化,我的复制过去了.

省略SettingKey

如果您不能(或不会)更改您的设计,可以指示autoFixture跳过设置SettingKey属性:

IMapPingXml sut = fixture    .Build<SettingMapPingXml>()    .Without(s => s.SettingKey)    .CreateAnonymous();

就个人而言,我发现每次需要一个特定类的实例时,都必须编写一个Build表达式.您可以解除实际实例化中如何创建SettingMapPingXml实例:

fixture.Customize<SettingMapPingXml>(    c => c.Without(s => s.SettingKey));IMapPingXml sut = fixture.CreateAnonymous<SettingMapPingXml>();

为了进一步,您可以在a Customization中封装该自定义方法调用.

public class SettingMapPingXmlCustomization : ICustomization{    public voID Customize(IFixture fixture)    {        fixture.Customize<SettingMapPingXml>(            c => c.Without(s => s.SettingKey));    }}

这需要您使用该自定义创建Fixture实例:

IFixture fixture = new Fixture()    .Customize(new SettingMapPingXmlCustomization())    .Customize(new automoqCustomization());

一旦你获得了两个或三个自定义链接,你可能会厌倦了一直写这个方法链.现在是时候将这些自定义内容封装到特定库的一组约定中:

public class TestConventions : CompositeCustomization{    public TestConventions()        : base(            new SettingMapPingXmlCustomization(),new automoqCustomization())    {    }}

这使您能够始终如下创建Fixture实例:

IFixture fixture = new Fixture().Customize(new TestConventions());

TestConventions为您提供了一个中心的地方,您可以随时修改测试套件的惯例,当您需要这样做时.它降低了单元测试的可维护性税,并有助于保持生产代码的设计更加一致.

最后,由于您看起来好像正在使用xUnit.net,您可以使用AutoFixture’s xUnit.net integration,但在此之前,您需要使用不太必要的 *** 纵Fixture的风格.事实证明,创建,配置和注入ISettings Test Double的代码是如此惯用,它有一个名为Freeze的快捷方式:

fixture.Freeze<Mock<ISettings>>()    .Setup(s => s.Get(settingKey)).Returns(xmlString);

有了这一点,下一步是定义一个自定义autoDataAttribute:

public class autoConventionDataAttribute : autoDataAttribute{    public autoConventionDataAttribute()        : base(new Fixture().Customize(new TestConventions()))    {    }}

您现在可以将测试减少到裸露的要素,摆脱所有的噪音,使测试简洁地表达只有重要的事情:

[Theory,autoConventionData]public voID ReducedTheory(    [FroZen]Mock<ISettings> settingsstub,SettingMapPingXml sut){    string xmlString = @"        <mapPings>            <mapPing source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />            <mapPing source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />        </mapPings>";    string settingKey = "gcCreditApplicationUsdFIEldMapPings";    settingsstub.Setup(s => s.Get(settingKey)).Returns(xmlString);    XElement actualXml = sut.GetXml();    XElement expectedXml = XElement.Parse(xmlString);    Assert.True(XNode.deepequals(expectedXml,actualXml));}

其他选项

要使原始测试通过,您还可以完全关闭自动属性:

fixture.OmitAutopropertIEs = true;
总结

以上是内存溢出为你收集整理的c# – AutoFixture / AutoMoq忽略注入的实例/冻结模拟全部内容,希望文章能够帮你解决c# – AutoFixture / AutoMoq忽略注入的实例/冻结模拟所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1260749.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存