Is it possible to use xUnit with LINQPad?

冷暖自知 提交于 2020-06-26 13:51:08

问题


Is it possible to use xUnit with LINQPad?

It would be great to be able to write some tests for concepts being designed in LINQPad first. It's easier than adding yet another ConsoleApp23423894238 just to be able to quickly write some unit tests.


回答1:


Possible? Sort of.

This is neither a main use case of LINQPad nor of unit testing.

Regarding LINQPad, you could use an existing test runner but probably the only ones that could be integrated with LINQPad are console runners. A test runner is not a trivial program. Try Xunit.Runner.LinqPad.

As for needing to "write some tests for concepts being designed", consider that you might not be getting enough out of your unit testing workflow if you don't find them valuable enough to create a project and keep it in source control. One way to get more value is to use Behavior-Driven Development (BDD) and if you want to write the requirements in a business-readable language, try something like SpecFlow.




回答2:


Two answers pointed me into the right direction, but the descriptions haven't been complete enough, so I had to experiment a bit. To save you that time, I am describing what I did; I have now working solutions for LinqPad 5 and LinqPad 6.

LinqPad 5 - for .NET

Based on what Tom wrote, I was able to get it working with LinqPad 5 (v5.42.01).

There are some more steps required, which aren't obvious and not explained in the accepted answer, so I'll describe what helped on my side.

First of all, you will need to use the paid version of LinqPad, because only then you are able to load NUGET packages.

To load the required packages in LinqPad, press F4. The query properties dialog opens up. In that dialog, click Add NUGET....

The NUGET dialog opens up. In the middle of the NUGET dialog, there is a search textbox. Search for xUnit.Net - once it got displayed, click Add to query, wait until it loaded, then click on Add namespaces. Then do the same for Xunit.Runner.LinqPad.

Then close the NUGET dialog, but keep the query properties open. You need to to there to the Advanced tab: Enable (tick-mark) "Copy all non-framework references to a single local folder." Now you can close the query properties dialog.

Import the following example into the query editor (C# Program mode):

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
    }

}

Run it and you will see the output of the test runner.

Running 3 of 3 tests...
Finished: 3 tests in 0,003s (0 failed, 0 skipped)

Modify the test to see how it looks like if a test fails by changing the assert in the Test_Fact() to Assert.False(1 == 1);, then run it again. It should show this time:

Running 3 of 3 tests...
[FAIL] UserQuery+A_XUnitSelfTest.Test_Fact: Assert.False() Failure
Expected: False
Actual: True
at Xunit.Assert.False(Nullable`1 condition, String userMessage)
at Xunit.Assert.False(Boolean condition)
at UserQuery.A_XUnitSelfTest.Test_Fact() in C:\Users...\AppData\Local\Temp\LINQPad5_oyxxqxbu\query_mxsmky.cs:line 62
Finished: 3 tests in 0,005s (1 failed, 0 skipped)


LinqPad 6 - for .NET Core

LinqPad 6 is different, because it is based on .NET Core and hence does require different libraries.

Start with this:

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
    }

}

Then add xUnit.net, xUnit.net [Core Unit Testing Framework] and xUnit.net [Runner Utility] and all their namespaces via the F4 dialog (details see LinqPad 5 sesion) and finally copy the XUnit runner source code as mentioned by @ramiroalvarez to the end of the xUnit test example above:

XUnit runner source code

Enable "copy all nuget assemblies to a single local folder" in the F4 dialog, Advanced tab ... that should be enough, but it isn't so read on:

If you try it, it throws the exception InvalidOperationException("Please enable the shadow folder option for none-framework references (F4 -> Advanced).") in the source code.

Now I looked into the XUnit runner, and found that if you comment out 2 lines of code then it is running fine:

Original code (located in method 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);

Comment the lines

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

and you're done! (Tested with v6.8.3 version of LinqPad 6, X64 version)


Hints:

Hint 1:
You can append the xUnit runner to your code as described, but there is a more elegant way in LinqPad 6: Save a copy containing only the xUnit.Runner source code in the same path as your unit test files with the file name xUnit.Runner.LinqPad6.linq. Then, add the following to the top of your test code:


Now you have cleaned up your code! Every time you create a new test, just remember to add the #load command.

Hint 2:
Add the following 2 attributes to the code of the test runner:

/// <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";
    }
}

This is useful, if you want to skip tests. To skip a test, simply replace [Fact] by [SkipFact] or [Theory] by [SkipTheory].

If you're busy writing a test and you want to test only that single test, set all attributes to [SkipFact] or likewise [SkipTheory], except the single one you're writing currenlty.

Then, to quickly run all the tests again from time to time - without having to change each and every single attribute - add the following to the initialization part of your test suite (see 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
    }
}

If you're done, restore the [Fact]/ [Theory] attributes and set the .OverrideSkip variables back to false.


More about unit testing

For some advanced topics about unit testing with xUnit, I recommend to read:

  • xunit tests in net core
  • global setup for startup and teardown
  • shared context
  • running tests in parallel
  • configuring with json
  • configuring with xml
  • skipping Test and Theory // description works for TheoryAttribute too

If you want to use it with Visual Studio, you can find infos here.




回答3:


Thanks to asherber, xunit now works with linqpad6 (netcore).

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

It is important within the query properties, check "copy all nuget assemblies to a single local folder" otherwise, you will have the following error "InvalidOperationException: Please enable the shadow folder option for none-framework references"




回答4:


I just tried to open a LINQPad query file that tried to load the xunit package and got an error which has never happened to me in using LINQPad for years. So my answer is no.



来源:https://stackoverflow.com/questions/52798462/is-it-possible-to-use-xunit-with-linqpad

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!