能否在LINQPad中使用xUnit?

6

可以在LINQPad中使用xUnit吗?

如果能够先在LINQPad中编写某些概念的测试,那将是非常好的。这比添加另一个ConsoleApp23423894238来快速编写单元测试要容易得多。


我看到付费版本可以获取NuGet包,特别是:https://www.nuget.org/packages/Xunit.Runner.LinqPad/ - robi-y
我已经添加了一个逐步说明,希望能帮到你...;-) - Matt
4个回答

14

是的,可以在LinqPad中使用xUnit。我已经更新了这个答案,为您提供最新的信息,但我之前给出的原始答案也为您提供了一些关于使用xUnit的见解和链接,因此我将其保留供您参考。


更新: 最新版本的LinqPad 6或7(自V 6.9.x及更高版本,参见6.9 Release Notes section现在已经通过新菜单项Query / Add XUnit Test Support内置了XUnit支持。这将添加一个查询和所需的样板代码通过#load引用,因此您可以立即开始测试事实和理论。
您可以通过按Alt+Shift+T来运行示例测试方法。
例如,在样板代码中添加以下新理论(放在主要方法之后,在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测试,除非您确切地知道自己在做什么,否则不要触碰它。例如,您可以修改此文件以更改测试摘要的显示方式(例如,您可以向方法RunTests添加参数Func<UserQuery.TestResultSummary, bool> filter = null并在查询内部添加.Where(filter),如果filter不为null,则允许您过滤测试摘要的结果)。

例如: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 is Xunit.Runners.TestSkippedInfo;

  3. 类 TestResultSummary / public object Status - 添加: _testInfo is Xunit.Runners.TestSkippedInfo ? Util.WithStyle ("Skipped", "color:orange") :

这些更改将使 xunit 脚本能够在结果中显示测试被跳过的情况。

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

[SkippableFact]
void Test_Xunit3()
{

Skip.If(true, &quot;skipping&quot;);

Assert.True(1 + 1 == 2);

}

[SkippableTheory]
[InlineData(1)]
[InlineData(2)]
void Test_Xunit4(int x)
{

Skip.If(x==2, &quot;skipping&quot;);

Assert.True(1 + 1 == 2);

}

注意: 这些更改也适用于普通的XUnit。因此,将更改添加到代码中不会对其造成影响。


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

两个答案指引了我正确的方向,但是描述不够完整,所以我不得不进行一些实验。为了节省您的时间,我将描述我所做的事情;我现在有适用于LinqPad 5和LinqPad 6(以及7)的工作解决方案。

LinqPad 5 - for .NET

基于Tom所写的内容,我能够让LinqPad 5(v5.42.01)正常工作。

还需要一些更多的步骤,这些步骤并不明显,也没有在被接受的答案中解释清楚,所以我会描述一下我是如何解决的。

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

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

NUGET对话框会打开。在NUGET对话框的中间,有一个搜索文本框。搜索xUnit.Net - 一旦它显示出来,点击Add to query,等待它加载完成,然后点击Add namespaces.然后对Xunit.Runner.LinqPad.做同样的操作。

然后关闭NUGET对话框,但保持查询属性打开。您需要在其中进入高级选项卡:启用(勾选)"将所有非框架引用复制到单个本地文件夹中。"现在可以关闭查询属性对话框了。

将以下示例导入到查询编辑器中(C#程序模式):

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个...
已完成:3个测试,用时0.003秒(0个失败,0个跳过)

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

运行3个测试中的3个...
[FAIL] UserQuery+A_XUnitSelfTest.Test_Fact: Assert.False() Failure
期望值:False
实际值:True
在Xunit.Assert.False(Nullable`1 condition, String userMessage)位置
在Xunit.Assert.False(Boolean condition)位置
在C:\Users...\AppData\Local\Temp\LINQPad5_oyxxqxbu\query_mxsmky.cs的UserQuery.A_XUnitSelfTest.Test_Fact()中的第62行
已完成:3个测试,用时0.005秒(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对话框(详见LinqPad 5会话)添加xUnit.netxUnit.net [Core Unit Testing Framework]xUnit.net [Runner Utility]及其所有命名空间,最后将XUnit运行器源代码由@ramiroalvarez提到复制到上面的xUnit测试示例末尾:

XUnit运行器源代码

在F4对话框的高级选项卡中启用“将所有Nuget程序集复制到单个本地文件夹”...那应该就足够了,但事实并非如此,请继续阅读:
如果您尝试它,则会抛出InvalidOperationException("Please enable the shadow folder option for none-framework references (F4 -> Advanced).")异常。
现在我查看了XUnit运行器,发现如果注释掉两行代码,则它可以正常运行:
原始代码(位于方法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);

然后你就完成了!(已在LinqPad 6版本v6.8.3 X64上测试)


提示:

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

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

提示 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],除了你当前正在编写的那个测试。
然后,为了每隔一段时间快速运行所有测试 - 而不必更改每个属性 - 将以下内容添加到测试套件的初始化部分(参见全局设置以进行启动和拆卸):
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
注意:如果您想根据运行时条件跳过事实,请查看动态跳过测试和理论

更多关于单元测试的内容

如果您想了解有关使用xUnit进行单元测试的高级主题,我建议阅读以下内容:

如果您想在Visual Studio中使用它,可以在这里找到信息。


我无法让它工作,因为我将我的脚本保存为Xunit.linq,从而覆盖了添加的xunit文件...使用另一个名称就可以正常工作 :) - Carra
@Carra - XUnit.linq是一个保留的名称,请勿使用。我写的是你可以修改那个小型库的代码来扩展它。但是你的测试应该始终存储在其他地方,而不是XUnit.linq中。我已经修改了文本以使其更加清晰明了。 - Matt
1
一开始不知道为什么它不能工作,但在阅读了您的文本后,发现它写了一个xunit.linq文件,我做错了什么就变得非常明显了。谢谢! - Carra

4

可能吗?有点。

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

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

至于需要“为正在设计的概念编写一些测试”,请考虑如果您没有发现它们足够有价值而创建一个项目并将其保留在源代码控制中,则可能无法从您的单元测试工作流程中获得足够的价值。获取更多价值的一种方法是使用行为驱动开发(BDD),如果您想以业务可读语言编写要求,请尝试类似于SpecFlow的东西。


1

谢谢你的回答,但是在我的端上我无法让它运行。你能否添加一些设置步骤,谢谢? - Matt
我解决了这个问题并更新了我的答案... XUnitRunner.cs 代码中有一个问题。 - Matt

-1

我刚试图打开一个LINQPad查询文件,尝试加载xunit,但出现了一个错误,这在我使用LINQPad多年来从未发生过。所以我的答案是


1
我已将此问题报告为LINQPad问题。我相信问题在于,正如xunit在他们的教程中所描述的那样,xunit包是一个NuGet元包。它本身没有任何内容;它只依赖于其他包。您可以在LINQPad中很好地安装这些其他包。 - Tom Blodget

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接