xUnit - 显示理论成员数据的测试名称 (TestCase)

xUnit - Display test names for theory memberdata (TestCase)

我一直在使用 NUnit 进行测试,我非常喜欢测试用例。在 NUnit 中,您可以使用 TestCaseData class.

中的 SetName 函数轻松设置测试用例中的每个测试名称

xUnit有类似的功能吗?

目前我在测试资源管理器中只能看到一个测试,即使我在测试用例中有 6 个测试。

x单元测试

public class LogHandler : TestBase
{
    private ILogger _logger;

    public LogHandler()
    {
        //Arrange
        LogAppSettings logAppSettings = GetAppSettings<LogAppSettings>("Log");

        IOptions<LogAppSettings> options = Options.Create(logAppSettings);

        LogService logService = new LogService(new Mock<IIdentityService>().Object, options);

        LogProvider logProvider = new LogProvider(logService);

        _logger = logProvider.CreateLogger(null);
    }

    public static IEnumerable<object[]> TestCases => new[]
    {
        new object[] { LogLevel.Critical,
            new EventId(),
            new Exception(),
            1 },
        new object[] { LogLevel.Error,
            new EventId(),
            new Exception(),
            1 },
        new object[] { LogLevel.Warning,
            new EventId(),
            new Exception(),
            0 },
        new object[] { LogLevel.Information,
            new EventId(),
            new Exception(),
            0 },
        new object[] { LogLevel.Debug,
            new EventId(),
            new Exception(),
            0 },
        new object[] { LogLevel.Trace,
            new EventId(),
            new Exception(),
            0 },
        new object[] { LogLevel.None,
            new EventId(),
            new Exception(),
            0 }
    };

    [Theory, MemberData(nameof(TestCases))]
    public void Test(LogLevel logLevel, EventId eventId, Exception exception, int count)
    {
        //Act
        _logger.Log<object>(logLevel, eventId, null, exception, null);

        //Assert
        int exceptionCount = Database.Exception.Count();

        Assert.Equal(exceptionCount, count);
    }
}

x单元测试window

这里应该是6个测试而不是一个! (忽略 GetOrganisationStatuses)。

N单元测试用例

public static IEnumerable TestDatabaseCases
{
    get
    {
        yield return new TestCaseData(LogLevel.Critical,
            new EventId(1),
            new Exception("Exception"),
            0,
            1).SetName("InsertException_Should_Insert_When_LogLevel_Critical");

        yield return new TestCaseData(LogLevel.Error,
            new EventId(1),
            new Exception("Exception"),
            0,
            1).SetName("InsertException_Should_Insert_When_LogLevel_Error");

        yield return new TestCaseData(LogLevel.Warning,
            new EventId(1),
            new Exception("Exception"),
            0,
            0).SetName("InsertException_Should_Not_Insert_When_LogLevel_Warning");

        yield return new TestCaseData(LogLevel.Information,
            new EventId(1),
            new Exception("Exception"),
            0,
            0).SetName("InsertException_Should_Not_Insert_When_LogLevel_Information");

        yield return new TestCaseData(LogLevel.Debug,
            new EventId(1),
            new Exception("Exception"),
            0,
            0).SetName("InsertException_Should_Not_Insert_When_LogLevel_Debug");
    }
}

N单元测试window

这就是我想要的 xUnit!

如何在xUnit中为测试用例中的每个测试设置名称?

Currently I can only see one test in the test explorer even tho I have 6 tests in the test case.

这是因为 xUnit.net 认为您的测试数据不可序列化。看到这个问题:https://github.com/xunit/xunit/issues/1473

重要的细节是:

The short answer

If some of your theory data can't be "serialized" by xUnit.net, then it cannot be encapsulated into the serialization of a test case which we're required to do for the Visual Studio test runner.

The long answer

In the Visual Studio test runner, test cases are discovered in one process, and executed in another. Therefore, test cases must be able to be turned into an unqualified string representation (aka, "serialized") in order to be run. We can also serialize at the test method level, because that just involves knowing the type and method name (both strings). When you start putting data into the mix, we need to ensure we know how to serialize that data; if we can't serialize all of the data for a theory, then we have to fall back to just a single method (which we know we can serialize).

完整答案包含更多细节。

In NUnit you can easily set each test name in the test case using the SetName function in a TestCaseData class.

Does xUnit have a similar function for this?

暂时没有。

实际上有一个可行的解决方案需要一些管道代码,这些代码应该可以与未更改的测试一起使用。它需要实现自定义 TheoryAttribute、自定义 TheoryDiscoverer 和自定义 TestCase 类。在这个 repo DjvuNet/DjvuNet.Shared.Tests.

中,可以根据 MIT 许可获得整个解决方案

实现所需的文件是: DjvuTheoryAttribute, DjvuTheoryDiscoverer, DjvuNamedDataRowTestCase, DjvuDataRowTestCase

用法很简单:编译以上文件,将它们直接包含到测试程序集中或作为单独的程序集,然后在代码中将它们用作 follows:

    [DjvuTheory]
    [ClassData(typeof(DjvuJsonDataSource))]
    public void DirmChunk_Theory(DjvuJsonDocument doc, int index)
    {
        int pageCount = 0;
        using (DjvuDocument document = DjvuNet.Tests.Util.GetTestDocument(index, out pageCount))
        {
            DjvuNet.Tests.Util.VerifyDjvuDocument(pageCount, document);
            DjvuNet.Tests.Util.VerifyDjvuDocumentCtor(pageCount, document);

            // DirmChunk is present only in multi page documents
            // in which root form is of DjvmChunk type
            if (document.RootForm.ChunkType == ChunkType.Djvm)
            {
                DirmChunk dirm = ((DjvmChunk)document.RootForm).Dirm;

                Assert.NotNull(dirm);

                Assert.True(dirm.IsBundled ? doc.Data.Dirm.DocumentType == "bundled" : doc.Data.Dirm.DocumentType == "indirect");

                var components = dirm.Components;
                Assert.Equal<int>(components.Count, doc.Data.Dirm.FileCount);
            }
        }
    }

其中一个理论函数参数在 xUnit 中不可序列化,但尽管理论测试将单独显示并编号。如果 theory 函数的第一个参数是字符串类型,除了作为函数调用的参数外,它将用作测试的名称。

这个想法归功于其他开发人员 - 我必须找到一个 link 他的代码 - 但它是为 DjvuNet 项目从头开始重新实现的。

这对我有用:

public class TestScenario
{
...
    public override string ToString()
    {
        return $"Role: {Role}...";
    }
}
    [Theory]
    [ClassData(typeof(MyProvider))]
    public void TestScenarios(TestScenario scenaro)
    {
        TestScenarioInternal(scenaro);

    }