Unit testing 如何在xunit/autofixture中组合PropertyData和AutoNSSubstituteData属性?

Unit testing 如何在xunit/autofixture中组合PropertyData和AutoNSSubstituteData属性?,unit-testing,mocking,xunit.net,autofixture,nsubstitute,Unit Testing,Mocking,Xunit.net,Autofixture,Nsubstitute,我使用的是[AutoNSubstituteData]属性,发布在这里: 我想将其与xunit extensions中的[PropertyData(“”)]属性结合起来 这是我的测试: public static IEnumerable<string[]> InvalidInvariant { get { yield return new string[] { null }; yield return new [] { string.E

我使用的是
[AutoNSubstituteData]
属性,发布在这里:

我想将其与xunit extensions中的
[PropertyData(“”)]
属性结合起来

这是我的测试:

public static IEnumerable<string[]> InvalidInvariant
{
    get
    {
        yield return new string[] { null };
        yield return new [] { string.Empty };
        yield return new [] { " " };
    }
}

[Theory, AutoNSubstituteData, PropertyData("InvalidInvariant")]
public void TestThatGuardsAreTriggeredWhenConnectionStringArgumentIsInvalid(
    IDeal deal,
    IDbConnection conn,
    IDb db,
    ISender sender,
    string invalidConnString,
    string query)
{
    deal.Init.Group.Returns(Group.A);
    deal.Aggr.Group.Returns(Group.A);
    deal.Product.Commodity.Returns(Product.Commodity.E);

    var sut = new Handler(db, sender);
    Assert.Throws<ArgumentException>(() => 
        sut.HandleDeal(deal, conn, invalidConnString, query));
}
公共静态IEnumerable变量
{
得到
{
返回新字符串[]{null};
返回新的[]{string.Empty};
返回新的[]{”“};
}
}
[理论、自动替代数据、属性数据(“无效变量”)]
连接字符串Guments无效时触发保护的公共无效测试(
理想的交易,
IDB连接连接器,
IDb db,
伊森德寄件人,
字符串invalidConnString,
字符串查询)
{
deal.Init.Group.Returns(Group.A);
交易累计组收益(A组);
交易.产品.商品.退货(产品.商品.E);
var sut=新处理程序(db,发送方);
Assert.Throws(()=>
sut.HandleDeal(交易,连接,无效连接字符串,查询));
}

是否有一种方法可以组合这些属性或获得所需的功能(模拟所有内容,除了应填充属性数据的
invalidConnstring

属性通过查找一个或多个“数据源属性”来工作;比如说

  • [InlineData]
  • [PropertyData]
  • [ClassData]
  • 等等
[AutoData]
属性只是另一个这样的属性,派生的
[AutoNSubstituteData]
属性也是如此

可以将多个“数据源属性”添加到同一个
[Theory]
,如
[InlineData]
属性的惯用用法所示:

[Theory]
[InlineData("foo")]
[InlineData("bar")]
[InlineData("baz")]
public void MyTest(string text)
这将生成三个测试用例

也可以将
[PropertyData]
[AutoData]
组合在一起,但是可能无法实现您希望它实现的功能。这:

[Theory]
[AutoNSubstituteData]
[PropertyData("InvalidInvariant")]
public void MyTest(/* parameters go here */)
将导致1+n测试用例:

  • 来自
    [AutoNSubstituteData]
  • n来自
    invalidVariant
    属性的测试用例
这两个属性互不了解,因此不能将它们组合在一起,因为它们彼此都知道

然而,当您实现一个属性时,您可以编写任何您想要的代码,包括使用
Fixture
实例,那么为什么不这样做呢

public static IEnumerable<string[]> InvalidInvariant
{
    get
    {
        var fixture = new Fixture().Customize(new MyConventions());
        // use fixture to yield values...,
        // using the occasional hard-coded test value
    }
}
这将导致第一个参数(
text
)由属性中的常量填充,而其余参数由AutoFixture填充

从理论上讲,您也可以使用
复合数据属性
组合
[AutoData]
[PropertyData]
属性,但这是不可能的


最后,您可以考虑使用真正的一流参数化测试。

< P>有两种方法:

选项1-使用和CompositeDataAttribute类:

internal class AutoNSubstituteDataAttribute : AutoDataAttribute
{
    internal AutoNSubstituteDataAttribute()
        : base(new Fixture().Customize(new AutoNSubstituteCustomization()))
    {
    }
}

internal class AutoNSubstitutePropertyDataAttribute : CompositeDataAttribute
{
    internal AutoNSubstitutePropertyDataAttribute(string propertyName)
        : base(
            new DataAttribute[] { 
                new PropertyDataAttribute(propertyName), 
                new AutoNSubstituteDataAttribute() })
    {
    }
}
定义测试用例如下:

public class Scenario
{
    public static IEnumerable<object[]> InvalidInvariantCase1
    {
        get
        {
            yield return new string[] { null };
        }
    }

    public static IEnumerable<object[]> InvalidInvariantCase2
    {
        get
        {
            yield return new string[] { string.Empty };
        }
    }

    public static IEnumerable<object[]> InvalidInvariantCase3
    {
        get
        {
            yield return new string[] { " " };
        }
    }
}
请注意,参数化参数
invalidConnString
必须为空

选项2-使用:

公共类场景
{
公共无效参数化测试(
理想的交易,
IDB连接连接器,
IDb db,
伊森德寄件人,
字符串invalidConnString,
字符串查询)
{
}
[第一课堂测试]
公共静态测试用例[]RunAParameterizedTest()
{
var testCases=new[]
{
新的
{
invalidConnString=(字符串)null
},
新的
{
invalidConnString=string.Empty
},
新的
{
invalidConnString=“”
}
};
var fixture=新fixture()
.Customize(新的AutoNSubstituteCustomization());
返回测试用例
.选择(tc=>
新测试用例(
s=>s.A参数化测试(
fixture.Create(),
fixture.Create(),
fixture.Create(),
fixture.Create(),
tc.INVALIDCONSTRING,
fixture.Create())
.ToArray();
}
}

我实现了一个
自动属性数据属性
,它将xUnit的
属性数据属性
与AutoFixture的
自动数据属性
相结合。我把它贴出来作为答复

在您的情况下,您需要以与从
AutoDataAttribute
继承相同的方式从属性继承,但传递夹具创建函数而不是实例的情况除外:

public class AutoNSubPropertyDataAttribute : AutoPropertyDataAttribute
{
    public AutoNSubPropertyDataAttribute(string propertyName)
        : base(propertyName, () => new Fixture().Customize(new AutoNSubstituteCustomization()))
    {
    }
}

哇!事实上,这几乎就是@MarkSeemann所写的,但在代码中:)希望能有所帮助。
public class Scenarios
{
    [Theory]
    [AutoNSubstitutePropertyData("InvalidInvariantCase1")]
    [AutoNSubstitutePropertyData("InvalidInvariantCase2")]
    [AutoNSubstitutePropertyData("InvalidInvariantCase3")]
    public void AParameterizedTest(
        string invalidConnString,
        IDeal deal,
        IDbConnection conn,
        IDb db,
        ISender sender,
        string query)
    {
    }
}
public class Scenario
{
    public void AParameterizedTest(
        IDeal deal,
        IDbConnection conn,
        IDb db,
        ISender sender,
        string invalidConnString,
        string query)
    {
    }

    [FirstClassTests]
    public static TestCase<Scenario>[] RunAParameterizedTest()
    {
        var testCases = new [] 
        {
            new 
            {
                invalidConnString = (string)null
            },
            new
            {
                invalidConnString = string.Empty
            },
            new
            {
                invalidConnString = " "
            }
        };

        var fixture = new Fixture()
            .Customize(new AutoNSubstituteCustomization());

        return testCases
            .Select(tc =>
                new TestCase<Scenario>(
                    s => s.AParameterizedTest(
                        fixture.Create<IDeal>(),
                        fixture.Create<IDbConnection>(),
                        fixture.Create<IDb>(),
                        fixture.Create<ISender>(),
                        tc.invalidConnString,
                        fixture.Create<string>())))
            .ToArray();
    }
}
public class AutoNSubPropertyDataAttribute : AutoPropertyDataAttribute
{
    public AutoNSubPropertyDataAttribute(string propertyName)
        : base(propertyName, () => new Fixture().Customize(new AutoNSubstituteCustomization()))
    {
    }
}