Custom Traits in xUnit

One of the implicit key characterstics which define the readability of any Unit Test cases is its ability to be grouped depending on multiple factors.

NUnit uses CategoryAttribute, while MSTest uses the TestCategoryAttribute for grouping tests. With xUnit, you could make use the TraitAttribute to achieve this. However, this is not short of problems of its own.

The most inconvenient part of using Traits are that they are basically a name-value pair of strings. This makes it vurnerable to typos or a headache when decide to change the name. Luckily, xUnit provides us an easy to use extensibility point.

ITraitAttribute and ITraitDiscoverer

You can create your own Custom Traits which could be used to decorate the test cases. For the sake of example, let us create two attributes – FeatureAttribute and BugAttribute which would be used to Categorize Tests cases for Features and Bugs.

[TraitDiscoverer(FeatureDiscoverer.TypeName,TraitDiscovererBase.AssemblyName)]
public class FeatureAttribute:Attribute, ITraitAttribute
{
    public string Id { get; set; }
    public FeatureAttribute(string id) => Id = id;
    public FeatureAttribute() { }
}

The attribute implements the ITraitAttribute interface and has a property to indicate the Id of the Feature. What is more interesting is the TraitDiscoverer attribute. It isn’t sufficient that we have the attributes, but it also needs to be discovered by the Test Explorer. This is where the TraitDiscoverer comes into place.

The attribute accepts two parameters, the fully qualified name of the Discoverer associated with the FeatureAttribute and the assembly which defines it. The Dicoverer for Feature is defined as

public class FeatureDiscoverer : TraitDiscovererBase,ITraitDiscoverer
{
    public const string TypeName = TraitDiscovererBase.AssemblyName + ".Helpers.CustomTraits.FeatureDiscoverer";

    protected override string CategoryName => "Feature";
    public override IEnumerable<KeyValuePair<string, string>> GetTraits(IAttributeInfo traitAttribute)
    {
        yield return GetCategory();
        var id = traitAttribute.GetNamedArgument<string>("Id");
        if (!string.IsNullOrEmpty(id))
        {
            yield return new KeyValuePair<string, string>(TypeName, id);
        }
    }
}

public class TraitDiscovererBase : ITraitDiscoverer
{
    public const string AssemblyName = "Nt.Infrastructure.Tests";
    protected const string Category = nameof(Category);
    protected virtual string CategoryName => nameof(CategoryName);

    protected KeyValuePair<string,string> GetCategory()
    {
        return new KeyValuePair<string, string>(Category, CategoryName);
    }
    public virtual IEnumerable<KeyValuePair<string, string>> GetTraits(IAttributeInfo traitAttribute)
    {
        return Enumerable.Empty<KeyValuePair<string,string>>();
    }
}

The Discoverer needs to be implement the ITraitDiscoverer which has a single method, GetTraits and returns a collection of KeyValuePairs. That’s all you need. Now you could decorate your Test Cases as the following

[Theory]
[MemberData(nameof(CreateMovieTest_ResponseStatus_200_TestData))]
[Feature("1523")]
public async Task CreateMovieTest_ResponseStatus_200(CreateMovieRequest request, CreateMovieResponse expectedResult)
{
    // Test Case
}

The above is whole lot cleaner than the following

[Trait("Category","Feature")]
[Trait("Feature","1523")]

C# 8.0 : Default Implementation of Interface Members

The way C# language is evolving is definitely exciting and one feature that truly makes the wait for C# 8.0 all the more exciting is default implementation of interfaces.

Since the onset, interfaces were behaviors characterized by member signatures, strictly disallowing any implementation. All that is going to change when C# 8.0 rolls out, as it supposedly, plans to support default implementation of interfaces. What it means is further trimming down the walls between interface and class (but remember, walls would still exist : Interface and Classes will still have significant difference, we will talk about it a bit later.)

Let’s look at an example, courtesy MSDN Blogs.

interface ILogger
{
void Log(LogLevel level, string message);
void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
public void Log(LogLevel level, string message) { ... }
// Log(Exception) gets default implementation
}

Doesn’t that look exciting ? Gone are days when as an Author of an interface, you were almost handicapped extending the interface fearing it might break any of implementing code. You could now provide the default implementation of your interface member, and still be assured that all implementing code works as fine as it was before.

If this doesn’t excite you, think from another perspective. What this implies is possibility of multiple inheritance (almost) in a language that was never really supported it. You could now provide two interfaces with default implementation, and have a class inherit from both.

interface IParentA
{
string MethodA() => nameof(MethodA);
}

interface IParentB
{
string MethodB() => nameof(MethodB);
}

class Child : IParentA,IParentB
{
void MethodChild()
{
Console.WriteLine($"{nameof(MethodA)} = {MethodA()}");
Console.WriteLine($"{nameof(MethodB)} = {MethodB()}");
}
}

This effectively brings in Multiple Inheritance in place. What would be interesting to see is how Microsoft circumvented the Diamond Problem. Let’s delve a bit into Diamond Problem first, before trying to understand Microsoft’s workaround from what ever we know as of now.

Diamond Problem

Let’s consider the following inheritance structure assuming C# were to support Multiple Inheritance.

class A
{
public virtual string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

class B : A
{
public override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

class C : A
{
public override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// Let's assume this works for sake of demonstration
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

In the above code, class D inherits from both Class B and Class C, both of which, in-turns inherits from Class A and overrides virtual member DemoMethod. Assuming this code compiles, what would be the output of Print() Method in Class D. Will it invoke method from Class B or Class C ? This ambiguity is better known as Diamond Problem.

Microsoft intents to work around the Diamond Problem here by taking the most specific override at the time. As Microsoft pens it down

“Diamonds with Classes

A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of that member at all.”

Let’s rewrite the above code using default implementation of interfaces.

interface A
{
string DemoMethod() => $"{nameof(A)}.{nameof(A.DemoMethod)}()";
}

interface B: A
{
override string DemoMethod() => $"{nameof(B)}.{nameof(B.DemoMethod)}()";
}

interface C: A
{
override string DemoMethod() => $"{nameof(C)}.{nameof(C.DemoMethod)}()";
}

// This would be Error as no most specific override for A.DemoMethod()
class D:B,C
{
public void Print()=> Console.WriteLine(DemoMethod());
}

// This would be Error as no most specific override for A.DemoMethod()
class E:B,C,A
{
string DemoMethod()=> B.base.DemoMethod();
public void Print()=> Console.WriteLine(DemoMethod());
}

So will now abstract class == interface ?

No, not really. Even though the differences are diminishing, there still lies a lot of difference between the two. To name a few
– Unlike Abstract Class ,you still can’t write a default constructor for interface.
– Covariance and Contravariance

Essentially, abstract class and interface would continue to coexist in its own space. What Microsoft has done with Default Implementation of Interface is making use of Traits (more on traits later.)