Extending XUnit Assert class with new asserts
Asked Answered
S

9

19

I'm trying to extend the xUnit assert method by adding some selenium functionality

namespace MyProject.Web.Specs.PageLibrary.Extensions
{
    public static class AssertExtensions
    {
        public static void ElementPresent(this Assert assert, ...)
        {
            if (...)
            {
                throw new AssertException(...);
            }
        }
    }
}

But I get this compile error when I try to use it.

using MyProject.Web.Specs.PageLibrary.Extensions;    
using Xunit;
...

public void DoSomething()
{
    Assert.ElementPresent(...);
}

And the error

Error   5   'Xunit.Assert' does not contain a definition for 'ElementPresent'

Does anyone know if this is possible or where I'm going wrong?

Stereo answered 24/4, 2013 at 14:42 Comment(0)
A
2

You need object intance that will be passed as this argument to extension method. In your case this would be correct syntax

var assert = new Assert();
assert.ElementPresent(...);

But I suppose you don't need or even can't create instance of Assert class.

What you are trying to do is call extension method as static invocation on extended class and that wont work. But why not simply call

 AssertExtensions.ElementPresent(...);
Attaboy answered 24/4, 2013 at 20:10 Comment(3)
Thanks, that explains why I couldn't get the assert to extend like I wanted. Your solution is pretty much what I ended up doing, but I called mine SeleniumAssert.ElementPresent(...);Stereo
For xUnit 2, check out the examples such as github.com/xunit/samples.xunit/blob/master/AssertExtensions/…Samal
This won't work because Assert's constructor is protected.Glum
I
15

Edit 2 xUnit 2 eventually ended up moving the assertions into a separate assembly altogether. There are both compiled and source only packages of this on NuGet, and the Assert class is partial, so by using the source only version of the package, Assert becomes very easily extensible (in C#, that is).

Edit For more completeness: xUnit 2 removes this extension point and recommends using extension methods along the lines of 'fluent' assertion libraries.


For completeness, here's a description of the "official" way of extending Assert (which surprisingly has not been mentioned at all, despite the fact that Brad Wilson even joined the discussion).

From version 1.5 (according to Brad's blog), xUnit.Extensions has explicit support for this via the Assertions and TestClass classes. It works like this:

TestClass has a property called Assert that is of type Assertions which relays all the methods on Xunit.Assert. Because TestClass.Assert is an instance, you can add methods to it through extension methods on Assertions:

public static class AssertionsExtensions
{
    public static void DeepEquals(this Assertions assertions, XNode expected, XNode actual)
    {
        assertions.True(XNode.DeepEquals(expected, actual)); // You can also use Assert.True here, there's effectively no difference.
    }
}

Now you need to have your test class derive from Xunit.Extensions.TestClass (confusingly, there is also Xunit.TestClass, which is not what you want), and the Assert property will "shadow" the Xunit.Assert type if you don't qualify the name explicitly.

In your test class that derives from TestClass, you can now use

Assert.DeepEquals(expectedXml, actualXml);

The only real difference from a built-in xUnit assertion (apart from the fact that syntax coloring for Assert is that of an identifier, not a type) is that when it fails, you simply get a TrueException, not a specific DeepEqualsException that could hypothetically tell you where the comparison failed. But of course you could build that too in the very same way.

Interpose answered 4/5, 2014 at 2:25 Comment(2)
Have a look at Ruben Bartelink's post.. below. Together with the knowledge up here it results in creating your own derivatoin from global::XUnit.AssertStaw
And for xUnit 2, the easiest thing is to check out the examples such as github.com/xunit/samples.xunit/blob/master/AssertExtensions/…Samal
W
15

Summary of the solution for xUnit 2. (Worked for me for version 2.1.0 from NuGet.)

Assert is a partial class that you can extend by adding another part. To be able to do that you need to compile the Assert assembly from sources. You can use xunit.assert.source NuGet to get the sources.

The Steps

  1. Remove reference to the xunit.assert NuGet package from the project.
  2. Instead install xunit.assert.source package.
  3. In the Xunit namespace, define public partial class Assert and add your custom asserts there.
  4. In your test project install the xunit.extensibility.execution package (or otherwise there will be a conflict between two different Assert classes and tests won't run becasue the xunit.execution.*.dll will be missing)

Example of a custom assert:

namespace Xunit
{ 
    public partial class Assert
    {
        public static void ArraySegmentEqual<T>(
            T[] expectedSequence, T[] buffer, int offset = 0)
        {
            for (int i = 0; i < expectedSequence.Length; i++)
            {
                int b = i + offset;

                True(buffer[b].Equals(expectedSequence[i]),
                    $"Byte #{b} differs: {buffer[b]} != {expectedSequence[i]}");
            }
        }
    }
}

Note: Other answers and edits also point to the solution, but it took me quite some tome to figure it out from there. Also, I do not claim this is the only or the best option.

Welty answered 18/3, 2016 at 15:32 Comment(2)
After you removing the xunit package it will stop running the test cases. Need to add xunit.core to up and running the test cases again.Bryantbryanty
Yes. Remove "xUnit", add "xUnit.Core" and "xUnit.Assert.Source".Samul
R
13

Sorry, but you're getting confused (EDIT: and so was I!). xUnit.net's Assert is static and thus cannot have extensions added (although other Assertion libraries do not sue this approach which is why one might expect to use Extension Methods to extend Assert). So in the xUnit.net universe, if you want to add a custom assertion, add a new static class with a different name.

You can make your approach work by changing your class from:

public static class AssertExtensions
{
    public static void ElementPresent(this Assert assert, ...)

to:

public class AssertExtensions : XUnit.Assert
{
    public static void ElementPresent(...)

and then using Brad Wilson's trick of adding:

using Assert = MyProject.Web.Specs.PageLibrary.Extensions.AssertExtensions; 

at the top of any file needing your extensions.

This technique is handy for adding overloads come to think of it....

(The obvious weakness is that you can't have more than one directly accessible via Assert. though)

Renny answered 24/4, 2013 at 23:56 Comment(9)
The Assert class isn't static but the methods inside are static, that's what was throwing me off. See the solution for @jureStereo
@Neil. Thanks for the downvote. I'm conversing with @xunit re seeing if Assert can become a static class to avoid anyone else running into this (it's a surprise to me that it isnt as xUnit.net depends on .NET 2.0 which allows static classes and I cant think how the change could be breaking.)Renny
@Stereo Ah, seems it may have been left open to enable the extension trick described here. Finally, the reason why I was so sure it was static - Brad Wilson himself told me it was:- xunit.codeplex.com/discussions/238521Renny
Sorry perhaps it was a bit harsh. +vote for the extra info though!Stereo
We consciously made Assert not be a static class so that it could support extensibility. You can add new assertions to a derived class and then always use the new class, perhaps via a using directive like: using Assert = MyNamespace.MyAssert;Abducent
@BradWilson Thanks for confirming that - once again there's not a lot in xUnit that isn't By Design :)Renny
@Stereo Incorporated an application of this technique into the answer. Have not compiled it - let me know how you get on (though there's still a lot to be said for keeping it simple and putting your own Assertions into a custom Assert class and using them via the real name so you can <strike>grep</strike>Select-String for them and/or tell them apart from xUnit.net built-ins.)Renny
@RubenBartelink I think I'll stick with my SeleniumAssert class that I am currently using, seems to be a neater solutiion and keeps it simpleStereo
Worked for me having the Namespace and name override and inheriting it from Assert. The unniceer thing is: Resharper puts up some twiggy lines to indicate that I do a static call using a member notion.Staw
A
2

You need object intance that will be passed as this argument to extension method. In your case this would be correct syntax

var assert = new Assert();
assert.ElementPresent(...);

But I suppose you don't need or even can't create instance of Assert class.

What you are trying to do is call extension method as static invocation on extended class and that wont work. But why not simply call

 AssertExtensions.ElementPresent(...);
Attaboy answered 24/4, 2013 at 20:10 Comment(3)
Thanks, that explains why I couldn't get the assert to extend like I wanted. Your solution is pretty much what I ended up doing, but I called mine SeleniumAssert.ElementPresent(...);Stereo
For xUnit 2, check out the examples such as github.com/xunit/samples.xunit/blob/master/AssertExtensions/…Samal
This won't work because Assert's constructor is protected.Glum
A
2

The problem is because of a simple encapsulation constraint:

Since the Assert class has the constructor set to protected you can't create an Extension Method for it, because you can't instantiate it.

So in order to extend Assert, you just have to inherit from it:

public class MyExtendedAssert : Assert
{
    public void ElementPresent(...)
    {
        ...
    }
}

And use:

MyExtendedAssert.ElementPresent(...);
Antivenin answered 7/4, 2019 at 7:6 Comment(0)
C
0

For xUnit 2+:

  1. Add xunit.assert.source Nuget package to the project with extensions.

  2. Create partial class definition:

namespace Xunit
{
  public partial class Assert
  {
    public static void ElementPresent(...)
    {
    }
  }
}
Coryza answered 2/2, 2018 at 15:31 Comment(0)
G
0

I use a simple partial helper where I add That property so that I can easily build extensions anywhere else:

// ReSharper disable once CheckNamespace
namespace Xunit
{
    public partial class Assert
    {
        [CanBeNull]
        public static Assert That => default;
    }
}

Read Premil's answer how to setup the projects.

Glum answered 21/5, 2019 at 14:21 Comment(0)
G
0

I was able to get the solution posted by @Premil to work, but I had to reference xunit.core rather than xunit.extensibility.execution.

Sample Visual Studio 2019 solution is located at https://github.com/groberts314/TestXUnitCustomAssertions.

Gomulka answered 20/1, 2022 at 14:12 Comment(0)
G
0

You can subclass Xunit.Assert and add additional static methods to your subclass.

I call my subclass Assert so that I don't need to rename any of the existing assertions and so I don't need to remember what my subclass's name is.

public class Assert : Xunit.Assert
{
    public static void DoesXyz()
    {
        // test for XYZ
        Assert.True(true);
    }
}

Then in your test:

public class Tests
{
    [Fact]
    public void CanUseDoesXyz()
    {
        Assert.DoesXyz();
    }
}
Gerdagerdeen answered 3/7, 2023 at 20:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.