As we all know, unit tests are a requirement of modern day development and writing unit tests for your analyzers couldn't be easier. There are a lot of helper methods provided by the Roslyn team to get you going, so there is no excuse to not test your analyzer.

In the Creating Your First Analyzer post, we discussed how to use the templates to create your first analyzer. When you used the template, one of the projects created is the MyFirstAnalyzer.Test project. If you look at the UnitTests.cs file, you'll notice there are two tests and a couple of methods that are overridden. You'll also notice that this test class derives from the CodeFixVerifier base class. If you only wanted to test your diagnostics, you can simply derive from the DiagnosticVerifier base class.

To start, lets look at the overridden methods:

protected override CodeFixProvider GetCSharpCodeFixProvider()
{
    return new MyFirstAnalyzerCodeFixProvider();
}

protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
{
    return new MyFirstAnalyzerAnalyzer();
}

The purpose of these two methods are to provide the base class with instances of the implementation of your diagnostic and verifier, so they can be tested. Failure to override these methods will result in failures when making the call to verify the diagnostic and code fix.

The other two methods in this class are unit tests. One is a negative test, ensuring the diagnostic does not trigger on a blank file. The other is a test that verifies the diagnostic is triggered at the correct location and it also executes the code fix and ensures that the fixed code matches the desired result. Let's start by looking into the portion that tests the diagnostic:

        var test = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class TypeName
    {   
    }
}";
        var expected = new DiagnosticResult
        {
            Id = MyFirstAnalyzerAnalyzer.DiagnosticId,
            Message = String.Format("Type name '{0}' contains lowercase letters", "TypeName"),
            Severity = DiagnosticSeverity.Warning,
            Locations =
                new[] {
                        new DiagnosticResultLocation("Test0.cs", 11, 15)
                    }
        };

        VerifyCSharpDiagnostic(test, expected);

As you can see, the code being tested is simply passed as a string to the VerifyCSharpDiagnostic method. The DiagnosticResult is created as the expectation for the output of the diagnostic. One thing I would change about this default behavior is that the Message string is hard-coded here as well as in the diagnostic. It may be better to share the strings across both the test and diagnostic, but that is a personal preference. Running the VerifyCSharpDiagnostic will ensure that the correct diagnostics are triggered at the expected locations.

Once we have verified that the diagnostics is triggered, we can also verify that the codefix works. So, in this example, the code fix code is very straightforward:

        var fixtest = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class TYPENAME
    {   
    }
}";
        VerifyCSharpFix(test, fixtest);

As you can see, it creates the desired code as a string and then passes in the original code and the desired code to the VerifyCSharpFix method. This method will verify that the passed in original code is correctly transformed to the fixed code after the code fix is applied.

Note: If you are working with Visual Basic code, you can use the equivalent VerifyBasicDiagnostic and VerifyBasicFix methods to test Visual Basic code. This can be very useful in cases where your diagnostic or code fix will result in different behavior for VB.Net code.

As you can see, writing tests for analyzers is very straightforward and (as usual) the Roslyn team has provided great tooling for getting your unit tests up an running very quickly.