Visual Studio [Test Traits]
Tags: codenewbie, csharp, dotnetcore, testing
Published: 2023-03-12 17:14:39

## Introduction

Learn how to categorize unit test methods which is beneficial when there are many test methods and or more than one category.

> **Note**
> The information presented only touches the surface, see the Microsoft TechNet article in the see also section for more details.

## Jumping in

Imagine having several hundred test methods that might be broken down into several categories, such as a category for data operations, another for API and so forth. In Visual Studio Test Explorer there are options to group test methods. 

![Test Explorer showing group bt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6vx3m3a9lvh1o3oo851.png)

To use the trait option add the TestCategory attribute to a test method as shown below for MSTest.

```csharp
[TestClass]
public partial class Dummy : TestBase
{

    [TestMethod]
    [TestCategory("EntityFrameworkCore")]
    public void SomeTest()
    {

    }
}
```

The same in NUnit

```csharp
[Test, Category("EntityFrameworkCore")]
public void SomeTest()
{

}
```

## Slight problem

This allow a developer to group by traits now yet there is an issue ready to bite the developer which is, what happens if the category name changes? The developer must go through all test and rename the category.

What can work for any unit test library is to create an enum with categories which if renamed can be refactored to all instances by Visual Studio.

```csharp
public enum Trait
{
    PlaceHolder,
    EntityFrameworkCore,
    Configurations,
    Controller,
    Services,
    StringHelpers,
    GenericMath,
    TimeOnly
}
```

For NUnit 

```csharp
[Test, Category(nameof(Trait.StringHelpers))]
```

For MSTest

```csharp
[TestMethod, TestCategory(nameof(Trait.StringHelpers))]
```

Consider creating a class project which stores the enumerations and place the binary in a folder easy to get too or better yet a local NuGet package.

Simple local feed

1. Create a package

1. Create a folder e.g. C:\NuGetLocalFeed

1. Under Visual Studio options for NuGet add the folder

Now when the package is needed, select the local feed. For a more complete method to create a local feed see my Code magazine article [Working with NuGet Local Packages](https://www.codemag.com/Article/2109091/Working-with-NuGet-Local-Packages).

## MSTest specific option

Using the enumeration above, add the following class to the class project or in a unit test project.

```csharp
/// <summary>
/// Declarative class for using Trait enum about for traits on test method.
/// </summary>
public class TestTraitsAttribute : TestCategoryBaseAttribute
{
    private readonly Trait[] _traits;

    public TestTraitsAttribute(params Trait[] traits)
    {
        _traits = traits;
    }

    public override IList<string> TestCategories => _traits.Select(trait 
        => Enum.GetName(typeof(Trait), trait)).ToList();
}
```
Now a developer can setup a test method as shown below.

```csharp
[TestMethod, TestTraits(Trait.StringHelpers)]
```
If this is appealing, see the project UnitTestHelperLibrary1 in the provided source code.

## See also

My Microsoft TechNet article
[Visual Studio: Different methods to display unit test](https://social.technet.microsoft.com/wiki/contents/articles/51303.visual-studio-different-methods-to-display-unit-test.aspx)

## Source code

Clone the following [GitHub repository](https://github.com/karenpayneoregon/unit-test-categories).

There are two unit test projects, one for MSTest and one for NUnit. 

### Class projects

- DataLibrary to provide a backend for testing EF Core
- TeamLibrary, several methods for the test methods
- UnitTestHelperLibrary contains enum mentioned above
- UnitTestHelperLibrary1 for MSTest only

### Bonus
Check out NaturalStringComparer method in TeamLibrary project for performing a natural sort on strings.

```csharp
[TestMethod, TestCategory(nameof(Trait.StringHelpers))]
public void NaturalStringSort()
{
    // arrange
    List<string> fileNames = new()
    {
        "Example12.txt", "Example1.txt", "Example2.txt", 
        "Example3.txt", "Example4.txt", "Example10.txt"
    };
    List<string> expected = new()
    {
        "Example1.txt", "Example2.txt", "Example3.txt", 
        "Example4.txt", "Example10.txt", "Example12.txt"
    };

    // act
    fileNames.Sort(new NaturalStringComparer());

    // assert
    CollectionAssert.AreEquivalent(fileNames, expected);

}
```