【问题标题】:Is it possible to use xUnit with LINQPad?是否可以将 xUnit 与 LINQPad 一起使用?
【发布时间】:2019-03-18 19:16:04
【问题描述】:

是否可以将 xUnit 与 LINQPad 一起使用?

如果能够首先为在 LINQPad 中设计的概念编写一些测试,那就太好了。这比添加另一个 ConsoleApp23423894238 只是为了能够快速编写一些单元测试更容易。

【问题讨论】:

标签: xunit linqpad xunit.net


【解决方案1】:

我刚刚尝试打开一个 LINQPad 查询文件,该文件试图加载 xunit package 并得到一个错误,这是我多年来使用 LINQPad 从未发生过的错误。所以我的回答是

【讨论】:

  • 我已将此报告为 LINQPad 问题。我认为问题在于,正如 xunit 在他们的教程中所描述的那样,xunit 包是一个 NuGet 元包。它本身没有任何内容;它仅取决于其他软件包。您可以在 LINQPad 中安装其他软件包就好了。
【解决方案2】:

可能吗?有点。

这既不是 LINQPad 的主要用例,也不是单元测试的主要用例。

关于 LINQPad,您可以使用现有的测试运行程序,但可能唯一可以与 LINQPad 集成的是控制台运行程序。测试运行程序不是一个简单的程序。试试Xunit.Runner.LinqPad

至于需要“为正在设计的概念编写一些测试”,请考虑如果您发现单元测试工作流程没有足够的价值来创建项目并将其保存在源代码管理中,那么您可能无法从单元测试工作流程中获得足够的收益.获得更多价值的一种方法是使用行为驱动开发 (BDD),如果您想用业务可读的语言编写需求,请尝试使用 SpecFlow 之类的方法。

【讨论】:

    【解决方案3】:

    是的,可以将 xUnit 与 LinqPad 一起使用。我已经更新了这个答案来给你最新的信息,但是我给你的原始答案也给了你一些关于 xUnit 使用的见解和链接,所以我保留它供你参考。


    更新: LinqPad 6 或 7 的最新版本(自 V 6.9.x 及更高版本,请参阅 6.9 Release Notes section 现在具有内置 XUnit 支持 通过新菜单项Query / Add XUnit Test Support。这将添加一个带有所需样板代码referenced via #load 的查询,以便您可以立即开始测试事实和理论。
    您可以通过按 Alt+Shift+T 来运行示例测试方法。

    例如,在样板代码中添加以下新理论(就在 main 方法之后,将其放在 private::Tests 区域内):

    #region private::Tests
    
    [Theory]
    [InlineData(1)]  
    [InlineData(3, 6)]  
    [InlineData(5, 7, 9)]
    void OddNumbers(params int[] values)
    {
        static bool IsOdd(int value) => value % 2 == 1;
        foreach (var value in values)
        {
            Assert.True(IsOdd(value), $"IsOdd({value}) is false");  
        } 
    }
    
    #endregion
    

    Alt+Shift+T 运行所有测试并检查结果。

    注意:它还会在您第一次添加 XUnit 测试支持时向您的查询目录添加一个文件 XUnit.linq - 这是一个支持 xUnit 测试的迷你库,您不应该触摸它 除非您确切地知道自己在做什么。例如,您可以修改此文件以更改测试摘要的显示方式(例如,您可以将参数 Func<UserQuery.TestResultSummary, bool> filter = null 添加到方法 RunTests 并在查询中添加 .Where(filter) 如果过滤器不为空。这允许您可以过滤测试摘要的结果)。

    例如:RunTests(filter: f => f.MethodName.StartsWith("Unit")); 只会在摘要中显示名称以“Unit”开头的测试。


    动态可跳过的单元测试

    您可以修改 LinqPad 6 提供的xunit 脚本以支持可跳过的单元测试(NUGET 包XUnit.SkippableFact,详细说明请参阅此答案末尾的“动态跳过测试和理论”链接)。

    在 xunit 中进行以下更改:

    1. 函数 RunTests - 添加: runner.OnTestSkipped = info => AddTestResult(info);

    2. 类 TestResultSummary - 添加: public bool Skipped() => _testInfo 是 Xunit.Runners.TestSkippedInfo;

    3. 类 TestResultSummary / 公共对象状态 - 添加: _testInfo 是 Xunit.Runners.TestSkippedInfo 吗? Util.WithStyle(“跳过”,“颜色:橙色”):

    这些更改将使xunit 脚本能够在结果中显示何时跳过测试。

    要测试它,通过F4加载NUGET包XUnit.SkippableFact,然后添加测试框架,最后添加测试:

    [SkippableFact]
    void Test_Xunit3()
    {
    
    Skip.If(true, "skipping");
    
    Assert.True(1 + 1 == 2);
    
    }
    
    [SkippableTheory]
    [InlineData(1)]
    [InlineData(2)]
    void Test_Xunit4(int x)
    {
    
    Skip.If(x==2, "skipping");
    
    Assert.True(1 + 1 == 2);
    
    }
    

    注意:这些更改也适用于普通的 XUnit。所以添加对代码的更改并没有什么坏处。


    现在,如果您愿意,请继续阅读原始答案(末尾有一些有用的提示):


    两个答案为我指明了正确的方向,但描述还不够完整,所以我不得不尝试一下。为了节省您的时间,我正在描述我的所作所为;我现在有适用于 LinqPad 5 和 LinqPad 6(和 7)的解决方案。

    LinqPad 5 - 用于 .NET

    根据 Tom 写的内容,我能够让它与 LinqPad 5 (v5.42.01) 一起使用。

    还需要一些额外的步骤,这些步骤并不明显,也没有在接受的答案中解释,所以我将描述对我有帮助的步骤。

    首先,您需要使用 LinqPad 的付费版本,因为只有这样您才能加载 NUGET 包。

    要在 LinqPad 中加载所需的包,请按 F4。查询属性对话框打开。在该对话框中,单击 添加 NUGET...

    NUGET 对话框打开。在 NUGET 对话框的中间,有一个搜索文本框。搜索xUnit.Net - 显示后,单击添加到查询,等到它加载完毕,然后单击添加命名空间。然后对Xunit.Runner.LinqPad.执行相同操作

    然后关闭 NUGET 对话框,但保持 查询属性 打开。您需要到 Advanced 标签:启用(勾选)“将所有非框架引用复制到单个本地文件夹。” 现在您可以关闭 查询属性对话框。

    将以下示例导入查询编辑器(C# Program 模式):

    void Main()
    {
         // Press F4, go to Advanced, then 
         // tick-mark "Copy all non-framework references to a single local folder"
         var me = Assembly.GetExecutingAssembly();
         var result = Xunit.Runner.LinqPad.XunitRunner.Run(me); 
         result.Dump("Xunit runner result");
    }
        
    /// <summary>
    /// This test class is there for demo purpose, so developers can
    /// see how to write test methods. 
    /// It also verifies that XUnit itself is functioning correctly.
    /// </summary>
    public class A_XUnitSelfTest
    {
    
        [InlineData(2)]
        [InlineData(3)]
        [Theory]
        public void Test_Theory(int a)
        {
            Assert.True(a == 2 || a == 3); // succeeds
        }
    
        [Fact]
        public void Test_Fact()
        {
            Assert.False(1 == 0); // succeeds
        }
    
    }
    

    运行它,您将看到测试运行器的输出。

    运行 3 个测试中的 3 个...
    已完成:0,003 秒内 3 次测试(0 次失败,0 次跳过)

    通过将Test_Fact() 中的断言更改为Assert.False(1 == 1); 来修改测试以查看测试失败的情况,然后再次运行它。这次应该会显示:

    运行 3 个测试中的 3 个...
    [失败] UserQuery+A_XUnitSelfTest.Test_Fact: Assert.False() 失败
    预期:错误
    实际:真实
    在 Xunit.Assert.False(Nullable`1 条件,字符串 userMessage)
    在 Xunit.Assert.False(布尔条件)
    在 C:\Users...\AppData\Local\Temp\LINQPad5_oyxxqxbu\query_mxsmky.cs: 62 行中的 UserQuery.A_XUnitSelfTest.Test_Fact()
    已完成:0,005 秒内进行 3 次测试(1 次失败,0 次跳过)


    LinqPad 6 - 用于 .NET Core

    LinqPad 6 不同,因为它基于 .NET Core,因此需要不同的库。

    从这里开始:

    void Main()
    {
         // Press F4, go to Advanced, then 
         // tick-mark "Copy all non-framework references to a single local folder"
         var me = Assembly.GetExecutingAssembly();
         var result = Xunit.Runner.LinqPad.XunitRunner.Run(me); 
         result.Dump("Xunit runner result");
    }
        
    /// <summary>
    /// This test class is there for demo purpose, so developers can
    /// see how to write test methods. 
    /// It also verifies that XUnit itself is functioning correctly.
    /// </summary>
    public class A_XUnitSelfTest
    {
    
        [InlineData(2)]
        [InlineData(3)]
        [Theory]
        public void Test_Theory(int a)
        {
            Assert.True(a == 2 || a == 3); // succeeds
        }
    
        [Fact]
        public void Test_Fact()
        {
            Assert.False(1 == 0); // succeeds
        }
    
    }
    

    然后通过F4对话框添加xUnit.netxUnit.net [Core Unit Testing Framework]xUnit.net [Runner Utility]及其所有命名空间(详情见LinqPad 5会话),最后将XUnit runner源代码as mentioned by @ramiroalvarez复制到上面的xUnit测试示例结束:

    XUnit runner source code

    在 F4 对话框的“高级”选项卡中启用“将所有 nuget 程序集复制到单个本地文件夹”......这应该足够了,但它不是那么阅读:

    如果你尝试它,它会在源代码中抛出异常 InvalidOperationException("请为非框架引用启用影子文件夹选项 (F4 -> 高级)。")。

    现在我查看了 XUnit runner, 发现如果你注释掉 2 行代码,那么它运行良好:

    原始代码(位于方法GetTargetAssemblyFilename):

    if (shadowFolder != xunitFolder 
        || Directory.GetFiles(shadowFolder, "xunit.execution.*.dll").Length == 0)
    {
        throw new InvalidOperationException("Please enable the shadow folder option ...");
    }
    
    // ...
    
    var targetAssembly = Path.Combine(shadowFolder, Path.GetFileName(assemblyFilename));
    //Console.WriteLine($"Copy \"{ assemblyFilename }\" -> \"{ targetAssembly }\"");
    File.Copy(assemblyFilename, targetAssembly, true);
    

    注释行

    // ...
    // throw new InvalidOperationException("Please enable the shadow folder option ...");
    // ...
    // File.Copy(assemblyFilename, targetAssembly, true);
    

    你就完成了! (测试用V6.8.3版本的LinqPad 6,X64版本)


    提示:

    提示 1:
    您可以按照描述将 xUnit 运行器附加到您的代码中,但在 LinqPad 6 中有一种更优雅的方式: 将仅包含 xUnit.Runner 源代码的副本保存在与单元测试文件相同的路径中,文件名为 xUnit.Runner.LinqPad6.linq。 然后,将以下内容添加到测试代码的顶部:


    现在你已经清理了你的代码!每次创建新测试时,只需记住添加#load 命令即可。

    提示 2:
    为了能够静态跳过事实,将以下 2 个属性添加到测试运行器的代码中:

    /// <summary>
    /// Skip a Fact (you can override it by setting OverrideSkip=true)
    /// </summary>
    public class SkipFactAttribute : FactAttribute
    {
        /// <summary>
        /// Override SkipFact: Set OverrideSkip=true to run even skipped tests
        /// </summary>
        public static bool OverrideSkip = false; // set to true to ignore skip
    
        /// <summary>
        /// Constructor. Used to set skip condition.
        /// </summary>
        public SkipFactAttribute()
        {
            if (!OverrideSkip) Skip = "Skipped Fact";
        }
    }
    
    /// <summary>
    /// Skip a Theory (you can override it by setting OverrideSkip=true)
    /// </summary>
    public class SkipTheoryAttribute : TheoryAttribute
    {
        /// <summary>
        /// Override SkipTheory: Set OverrideSkip=true to run even skipped tests
        /// </summary>
        public static bool OverrideSkip = false; // set to true to ignore skip
    
        /// <summary>
        /// Constructor. Used to set skip condition.
        /// </summary>
        public SkipTheoryAttribute()
        {
            if (!OverrideSkip) Skip = "Skipped Theory";
        }
    }
    

    如果您想跳过测试,这很有用。要跳过测试,只需将 [Fact] 替换为 [SkipFact] 或将 [Theory] 替换为 [SkipTheory]

    如果您正忙于编写测试并且只想测试单个测试,请将所有属性设置为 [SkipFact] 或同样设置为 [SkipTheory],除了您正在编写的单个测试。

    然后,为了不时再次快速运行所有测试 - 无需更改每个属性 - 将以下内容添加到测试套件的初始化部分(请参阅 global setup for startup and teardown):

    public class TestsFixture : IDisposable
    {
    
        public TestsFixture() // running once before the tests
        {
            // true: Ignore Skip and run all tests, false: Skip is effective
            SkipFactAttribute.OverrideSkip = true;
            SkipTheoryAttribute.OverrideSkip = true;
        }
    
        public void Dispose() // running once, after the tests
        {
             // teardown code
        }
    }
    

    如果完成,请恢复 [Fact]/[Theory] 属性并将 .OverrideSkip 变量设置回 false

    注意:如果您想跳过基于运行时条件的事实,请查看dynamically skipping test and theory


    更多关于单元测试

    有关使用 xUnit 进行单元测试的一些高级主题,我建议阅读:

    如果你想在 Visual Studio 中使用它,你可以找到 infos here

    【讨论】:

    • 我无法让它工作,因为我将我的脚本保存为 Xunit.linq,因此覆盖了添加的 xunit 文件...使用另一个名称可以正常工作:)
    • @Carra - XUnit.linq 是一个保留名称,不要使用它。我写的是你可以修改那个迷你库的代码来扩展它。但是您的测试应该始终存储在其他地方,而不是 XUnit.linq。已修改文本以使其更清晰。
    • 无法弄清楚为什么它不起作用,但是在阅读了您的文本后,它写入了一个 xunit.linq 文件,很明显我做错了什么。谢谢!
    【解决方案4】:

    感谢 asherber,xunit 现在可以与 linqpad6 (netcore) 一起使用。

    https://github.com/asherber/Xunit.Runner.LinqPad/blob/master/Xunit.Runner.LinqPad/XunitRunner.cs

    在查询属性中很重要,勾选“将所有nuget程序集复制到单个本地文件夹”,否则会出现以下错误“InvalidOperationException:请为非框架引用启用影子文件夹选项”

    【讨论】:

    • 感谢您的回答,但我无法说服它运行。能否请您添加一些设置步骤?
    • 我解决了它并更新了我的答案...... XUnitRunner.cs 代码中存在问题。
    猜你喜欢
    • 2017-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    • 2021-11-12
    • 2016-04-01
    • 2011-01-20
    • 2018-08-11
    相关资源
    最近更新 更多