在XUnit测试中使用AutoData和MemberData属性

时间:2016-06-29 01:00:46

标签: unit-testing xunit autofixture

我面临一个有趣的问题。我发现AutoDataAttribute可用于最小化" Arrange"测试的一部分(通过ctor传递的依赖关系)。真棒!

示例:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    { }
}

[Theory, AutoMoqData]
public void Process_ValidContext_CallsK2Workflows(
    [Frozen]Mock<IK2Datasource> k2,
    [Frozen]Mock<IAppConfiguration> config,
    PrBatchApproveBroker sut)
{
   (...)
}

现在我想使用这个伟大的功能将自己的数据注入到这个理论中:

[Theory, AutoMoqData, MemberData("Data")]
public void ExtractPayments_EmptyInvoiceNumber_IgnoresRecordsWithEmptyInvoiceNumber(
        [Frozen]Mock<IExcelDatasource> xls,
        SunSystemExcelDatasource sut,
        List<Row> rows,
        int expectedCount)
{
    (...)
}

问题:AutoData属性会为我生成随机数据。我发现的唯一方法是摆脱AutoData属性并使用MemberData。如果我这样做,我需要自己处理对象实例化:)...

  

有没有办法传递我的课程一些&#34;硬编码&#34;数据同时?

谢谢你, SEB

2 个答案:

答案 0 :(得分:6)

  

有没有办法传递我的课程一些&#34;硬编码&#34;数据同时?

这样做的一种方法是通过属性提供一些内联值,并让AutoFixture填充其余部分。

[Theory, InlineAutoMoqData(3)]
public void ExtractPayments_EmptyInvoiceNumber_IgnoresRecordsWithEmptyInvoiceNumber(
    int expectedCount,
    [Frozen]Mock<IExcelDatasource> xls,
    SunSystemExcelDatasource sut,
    List<Row> rows)
{
    // expectedCount is 3.
}

请注意,我必须移动expectedCount才能成为第一个参数,并使用定义为的自定义InlineAutoMoqData属性:

internal class AutoMoqDataAttribute : AutoDataAttribute
{
    internal AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}

internal class InlineAutoMoqDataAttribute : CompositeDataAttribute
{
    internal InlineAutoMoqDataAttribute(params object[] values)
        : base(
              new DataAttribute[] { 
                  new InlineDataAttribute(values),
                  new AutoMoqDataAttribute() })
    {
    }
}

另请参阅this帖子和this一些其他示例。

答案 1 :(得分:1)

您必须创建自己的自定义DataAttribute。这就是你如何撰写的。

/// <summary>
/// Helper DataAttribute to use the regular xUnit based DataAttributes with AutoFixture and Mocking capabilities.
/// </summary>
/// <seealso cref="Xunit.Sdk.DataAttribute" />
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AutoCompositeDataAttribute : DataAttribute
{
    /// <summary>
    /// The attributes
    /// </summary>
    private readonly DataAttribute baseAttribute;

    /// <summary>
    /// The automatic data attribute
    /// </summary>
    private readonly DataAttribute autoDataAttribute;

    /// <summary>
    /// Initializes a new instance of the <see cref="AutoCompositeDataAttribute" /> class.
    /// </summary>
    /// <param name="baseAttribute">The base attribute.</param>
    /// <param name="autoDataAttribute">The automatic data attribute.</param>
    public AutoCompositeDataAttribute(DataAttribute baseAttribute, DataAttribute autoDataAttribute)
    {
        this.baseAttribute = baseAttribute;
        this.autoDataAttribute = autoDataAttribute;
    }

    /// <summary>
    /// Returns the data to be used to test the theory.
    /// </summary>
    /// <param name="testMethod">The method that is being tested</param>
    /// <returns>
    /// One or more sets of theory data. Each invocation of the test method
    /// is represented by a single object array.
    /// </returns>
    public override IEnumerable<object[]> GetData(MethodInfo testMethod)
    {
        if (testMethod == null)
        {
            throw new ArgumentNullException(nameof(testMethod));
        }

        var data = this.baseAttribute.GetData(testMethod);

        foreach (var datum in data)
        {
            var autoData = this.autoDataAttribute.GetData(testMethod).ToArray()[0];

            for (var i = 0; i < datum.Length; i++)
            {
                autoData[i] = datum[i];
            }

            yield return autoData;
        }
    }
}



/// <summary>
/// Member auto data implementation based on InlineAutoDataAttribute and MemberData
/// </summary>
/// <seealso cref="Ploeh.AutoFixture.Xunit2.CompositeDataAttribute" />
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MemberAutoDataAttribute : AutoCompositeDataAttribute
{
    /// <summary>
    /// The automatic data attribute
    /// </summary>
    private readonly AutoDataAttribute autoDataAttribute;

    /// <summary>
    /// Initializes a new instance of the <see cref="MemberAutoDataAttribute" /> class.
    /// </summary>
    /// <param name="memberName">Name of the member.</param>
    /// <param name="parameters">The parameters.</param>
    public MemberAutoDataAttribute(string memberName, params object[] parameters)
        : this(new AutoDataAttribute(), memberName, parameters)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MemberAutoDataAttribute" /> class.
    /// </summary>
    /// <param name="autoDataAttribute">The automatic data attribute.</param>
    /// <param name="memberName">Name of the member.</param>
    /// <param name="parameters">The parameters.</param>
    public MemberAutoDataAttribute(AutoDataAttribute autoDataAttribute, string memberName, params object[] parameters)
        : base((DataAttribute)new MemberDataAttribute(memberName, parameters), (DataAttribute)autoDataAttribute)
    {
        this.autoDataAttribute = autoDataAttribute;
    }

    /// <summary>
    /// Gets the automatic data attribute.
    /// </summary>
    /// <value>
    /// The automatic data attribute.
    /// </value>
    public AutoDataAttribute AutoDataAttribute => this.autoDataAttribute;
}

如果要启用Moq,请将其扩展为

/// <summary>
/// The member auto moq data attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MemberAutoMoqDataAttribute : MemberAutoDataAttribute
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MemberAutoMoqDataAttribute"/> class.
    /// </summary>
    /// <param name="memberName">Name of the member.</param>
    /// <param name="parameters">The parameters.</param>
    public MemberAutoMoqDataAttribute(string memberName, params object[] parameters)
        : base(new AutoMoqDataAttribute(), memberName, parameters)
    {
    }
}