【问题标题】:Xunit 2.3.0 Unable to pass dates as inline paramsXunit 2.3.0 无法将日期作为内联参数传递
【发布时间】:2025-11-24 22:05:03
【问题描述】:

在 xUnit 2.2 和之前的版本中,我们能够在实现理论时将日期字符串作为内联数据传递。

[Theory]
[InlineData("title 1", "testing 1", 1, "Educational", "2017-3-1", "2018-12-31")]
[InlineData("title 2", "testing 2", 2, "Self Employment", "2017-2-1", "2018-2-28")]
public async Task WhenPassingCorrectData_SuccessfullyCreate(
    string title,
    string description,
    int categoryId,
    string category,
    DateTime startDate,
    DateTime endDate)
{

}

但是随着 2.3 的更新,这似乎被打破了,Visual Studio 给出了一个编译错误。

该值不能转换为方法参数'startDate'的 输入'System.DateTime

有没有人解决这个问题,必须将日期作为字符串接收并将它们转换到测试方法中?

这会是这个版本中的一个临时错误,会在未来的版本中修复吗?

PS:我在 VS2017 上的 .netcore 项目中使用 xUnit

【问题讨论】:

  • 不知道它曾经这样做过(您希望它猜测什么时区和/或 datetimekind 是否会神奇地解析它们 - 如果您希望它这样做呢DateTimeOffset ?)。我建议用 yields 他们作为 DateTimes 的 MemberData 替换它
  • 它被解析为一个日期时间,其中 Kind 不特定,并且在大多数情况下已经足够好了(至少在我当前的项目中)。它相当于构造一个日期时间,DateTime dt = new DateTime(2011, 1, 1)。是的,获取 DateTimeOffset 无济于事。你能举一个你提到的方法的例子吗?

标签: c# xunit xunit.net


【解决方案1】:

您可以使用MemberDataAttribute 明确表示:-

public static readonly object[][] CorrectData =
{
    new object[] { "title 1", "testing 1", 1, "Educational", new DateTime(2017,3,1), new DateTime(2018,12,31)},
    new object[] { "title 2", "testing 2", 2, "Self Employment", new DateTime(2017, 2, 1), new DateTime(2018, 2, 28)}
};

[Theory, MemberData(nameof(CorrectData))]
public async Task WhenPassingCorrectData_SuccessfullyCreate(string title, string description, int categoryId, string category, DateTime startDate, DateTime endDate)
{

}

(您也可以让属性返回IEnumerable<object[]>,通常使用yield return enumerator syntax 执行此操作,但我相信以上是目前C# 必须提供的最清晰的语法)

【讨论】:

  • 太棒了!这样就可以了,谢谢!
  • 这对我有帮助..谢谢。
  • 如果我不公开 CorrectData,则会出现异常。请修复。
  • 这太可怕了。这意味着我们不能使用实际的类。我们需要将数据作为一个松散的数组传入。
  • @RubenBartelink 当然。看看我的回答。我在这里写了一篇文章:christianfindlay.com/2019/06/30/xunit-strongly-typed-test-data
【解决方案2】:

目前更好的方法是使用 TheoryData 以便您可以使用强类型输入。 Creating strongly typed xUnit theory test data with TheoryData

TheoryData<DateTime> MemberData = new TheoryData<DateTime>
{
 DateTime.Now, 
 new DateTime(), 
 DateTime.Max
}

【讨论】:

  • 据我所知这是正确的答案,也许 ClassData 的语法糖后来出现了,但非常感谢!
  • 我投了反对票,因为该链接不再有效,我必须访问 Google 网络缓存才能阅读这篇文章。您的示例未显示如何将此 MemberData 传递给单元测试
  • 赞成,链接似乎有效。非常优雅的解决方案。
【解决方案3】:

这是将强类型测试数据传递到 xUnit 测试的好方法

Blog Post

Source Code

public class SampleData
{
    public int A { get; set; }
    public int B { get; set; }
    public int C => A + B;
}

public class UnitTest1
{
    /// <summary>
    /// The test data must have this return type and should be static
    /// </summary>
    public static IEnumerable<object[]> TestData
    {
        get
        {
            //Load the sample data from some source like JSON or CSV here.
            var sampleDataList = new List<SampleData>
            {
                new SampleData { A = 1, B = 2 },
                new SampleData { A = 3, B = 2 },
                new SampleData { A = 2, B = 2 },
                new SampleData { A = 3, B = 23 },
                new SampleData { A = 43, B = 2 },
                new SampleData { A = 3, B = 22 },
                new SampleData { A = 8, B = 2 },
                new SampleData { A = 7, B = 25 },
                new SampleData { A = 6, B = 27 },
                new SampleData { A = 5, B = 2 }
         };

            var retVal = new List<object[]>();
            foreach(var sampleData in sampleDataList)
            {
                //Add the strongly typed data to an array of objects with one element. This is what xUnit expects.
                retVal.Add(new object[] { sampleData });
            }
            return retVal;
        }
    }

/* Alternate form

    public static IEnumerable<object[]> TestData() 
    {
        yield return new [] { new SampleData { A = 1, B = 2 } };
        yield return new [] { new SampleData { A = 3, B = 2 } };
        yield return new [] { new SampleData { A = 2, B = 2 } };
        yield return new [] { new SampleData { A = 3, B = 23 } };
        yield return new [] { new SampleData { A = 43, B = 2 } };
        yield return new [] { new SampleData { A = 3, B = 22 } };
        yield return new [] { new SampleData { A = 8, B = 2 } };
        yield return new [] { new SampleData { A = 7, B = 25 } };
        yield return new [] { new SampleData { A = 6, B = 27 } };
        yield return new [] { new SampleData { A = 5, B = 2 } };
    }
*/

/* Or:

    public static IEnumerable<object[]> TestData() =>
        from x in new[] { 
            new SampleData { A = 1, B = 2 },
            new SampleData { A = 3, B = 2 },
            new SampleData { A = 2, B = 2 },
            new SampleData { A = 3, B = 23 },
            new SampleData { A = 43, B = 2 },
            new SampleData { A = 3, B = 22 },
            new SampleData { A = 8, B = 2 },
            new SampleData { A = 7, B = 25 },
            new SampleData { A = 6, B = 27 },
            new SampleData { A = 5, B = 2 } }
        select new object[] { x};
    */

    /// <summary>
    /// Specify the test data property with an attribute. This method will get executed 
    /// for each SampleData object in the list
    /// </summary>
    [Theory, MemberData(nameof(TestData))]       
    public void Test1(SampleData sampleData)
    {
        Assert.Equal(sampleData.A + sampleData.B, sampleData.C);
    }
}

【讨论】:

  • 上面的代码并不代表你最终得到的实际代码。它分为两个步骤,因为您想要反序列化数据,或者通过其他方式将数据加载到强类型列表中。 foreach 循环的第二部分是将加载的数据转换为 xUnit 期望的格式的部分。这在我发布的替代版本中丢失了。但是,如果您将测试数据构建到类中,这种替代形式就足够公平了。
  • 另一种方式可能是:new List(sampleDataList.Select(t => new object[] { t }));
【解决方案4】:

这个错误似乎在 v2.4.0+ 中得到修复(这个功能至少在 v2.3.1 中停止工作?)

如果无法将 xunit 升级到带有修复程序的版本,那么可能会执行与 xunit 隐式执行的等效操作:

https://github.com/xunit/xunit/blob/main/src/xunit.v3.core/Sdk/Reflection/Reflector.cs#L63-L69

[Theory]
[InlineData("title 1", "testing 1", 1, "Educational", "2017-3-1", "2018-12-31")]
[InlineData("title 2", "testing 2", 2, "Self Employment", "2017-2-1", "2018-2-28")]
public async Task WhenPassingCorrectData_SuccessfullyCreate(
    string title,
    string description,
    int categoryId,
    string category,
    string startDate, // <-- change from `DateTime` to `string`
    string endDate)   // <-- change from `DateTime` to `string`
{
    var expectedStartDate = DateTime.Parse(startDate); // <-- add this
    var expectedEndDate = DateTime.Parse(endDate);     // <-- add this

    //  rest of test ...

}

否则,如果有更复杂的测试,那么可能会按照其他人的建议使用MemberDataAttribute

【讨论】:

  • 谢谢,是的,它正在工作!我正在使用 xUnit v2.4.1 不知道修复。