Wednesday, July 22, 2020

PostSharp Contracts

One of the things PostSharp does well is remove boilerplate code. That's the repetitive stuff we hate typing and which clutters up and obscures the real code.

Contracts replace parameter and property validation. Let's consider a class that represents a box. It has length, height, and width properties that must be greater than zero - otherwise the box doesn't exist!

The PostSharp Contracts documentation is good, but I couldn't find a list of all the contracts anywhere so here's the ones I could find.

  • CreditCard
  • EmailAddress
  • EnumDataType
  • GreaterThan
  • LessThan
  • Negative
  • NotEmpty
  • NotNull
  • Phone
  • Positive
  • Range
  • RegularExpression
  • Required
  • StrictlyGreaterThan
  • StrictlyLessThan
  • StrictlyNegative
  • StrictlyPositive
  • StrictRange
  • StringLength
And, of course, you can create custom contracts.


Start a new Visual Studio project called Box. I'm using 2019, C#, and .Net Framework. Rename Class1 to Box. The initial code for Box is...

namespace Box
{
    public class Box
    {
        public int length { get; set; }
        public int width { get; set; }
        public int height { get; set; }

        public Box() { }
        public static Box CreateBoxFromLengthWidthHeight(int length, int width, int height)
        {
            return new Box() { length = length, height = height, width = width };
        }
    }
}

We would normally write validation logic into the property setters but this gets messy.

        private int _length;
        public int length
        {
            get { return _length; }
            set
            {
                if (value <= 0)
                    throw new System.Exception("length must be greater than zero");
                else
                    _length = value;
            }
        }

PostSharp allows us to remove this boilerplate code with contracts. Use NuGet to install postsharp.patterns.common and add your license to the project. Now add some annotations to the code like this...

using PostSharp.Patterns.Contracts;
namespace Box
{
    public class Box
    {
        [StrictlyGreaterThan(0)]
        public int length { get; set; }

        [StrictlyGreaterThan(0)]
        public int width { get; set; }

        [StrictlyGreaterThan(0)]
        public int height { get; set; }

        public Box() { }
        public static Box CreateBoxFromLengthWidthHeight(int length, int width, int height)
        {
            return new Box() { length = length, height = height, width = width };
        }
    }
}

We can use xUnit to test that this works. Use NuGet to add xunit and xunit.runner.visualstudio. Your solution's references should look like this.



Now add a test class to the Box.cs file. I put it inside the Box namespace.

using PostSharp.Patterns.Contracts;
using Xunit;

namespace Box
{
    public class Box
    {
        [StrictlyGreaterThan(0)]
        public int length { get; set; }

        [StrictlyGreaterThan(0)]
        public int width { get; set; }

        [StrictlyGreaterThan(0)]
        public int height { get; set; }

        public Box() { }

        public static Box CreateBoxFromLengthWidthHeight(int length, int width, int height)
        {
            return new Box() { length = length, height = height, width = width };
        }
    }

    public class TestBox
    {
        [Theory]
        [InlineData(1,1,1)]
        [InlineData(100,100,100)]
        [InlineData(0,1,1)]
        [InlineData(1,0,1)]
        [InlineData(1,1,0)]
        public void TestBoxConstructor(int length, int width, int height)
        {
            Box box = Box.CreateBoxFromLengthWidthHeight(length, width, height);
        }
    }
}


If we browse to the test Explorer and run the tests we can see that each of our contracts gets broken in turn.



Of course we really want all our tests to pass so let's rewrite the TestBox class and split it into a set of tests that should pass and a set of tests that should throw an exception...


    public class TestBox
    {
        [Theory]
        [InlineData(1,1,1)]
        [InlineData(100,100,100)]
        public void TestBoxConstructorPass(int length, int width, int height)
        {
            Box box = Box.CreateBoxFromLengthWidthHeight(length, width, height);
        }

        [Theory]
        [InlineData(0, 1, 1)]
        [InlineData(1, 0, 1)]
        [InlineData(1, 1, 0)]
        public void TestBoxConstructorException(int length, int width, int height)
        {
            Box box;
            Assert.Throws<ArgumentOutOfRangeException>(() => box = Box.CreateBoxFromLengthWidthHeight(length, width, height));
        }
    }



Tuesday, July 21, 2020

xUnit walkthrough

There are three popular unit test frameworks for Visual Studio - nUnit, MSTest, and xUnit. I've never looked at xUnit before, but people seem to be trending towards it. To be honest, there doesn't seem to be much daylight between the three frameworks, but I do like the syntax of xUnit slightly better.

Let's walk through an implementation of xUnit using the Visual Studio runner (test container) because, quite frankly, the console runner is a joke.

Start a new Visual Studio class library project. I'm using 2019, C#, and the .Net Framework. Call the project ClassXUnit.

We need to add xUnit and the Visual Studio xUnit runner. Open the NuGet manager (Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution), select Browse, and Search for and Install xunit.


Repeat for xunit.runner.visualstudio.

We will create a static class called Arithmetic with a single method called Add. Note the methods must have enough scope for us to call them from the test classes. It doesn't have to be a static class, but a non-static Arithmetic class makes no sense.


namespace ClassXUnit
{
    public static class Arithmetic
    {
        public static int Add(int a1, int a2)
        {
            return a1 + a2;
        }
    }
}

Unit tests take two forms [Fact] or [Theory]. A [Fact] test always runs exactly the same way. For example...


    public class TestArithmetic
    {
        [Fact]
        public void AddTest1()
        {
            Assert.Equal(4, Arithmetic.Add(2, 2));
        }
    }


Right-click on the solution and select "Run Tests". This opens the Test Explorer which will run all the tests (one so far) and tell us if the asserts passed or failed. You can drill down to see why a test failed, which we will do later.


Break the Add method so the assert fails...

             return a1 - a2;

and run the test again.


Fix the add method and run the test again to make sure it's working now.

The [Theory] tests are very useful. They allow you to run the same test repeatedly with different parameters. Add this to the TestArithmetic class.


        [Theory]
        [InlineData(2, 2, 4)]
        [InlineData(2, -1, 1)]
        [InlineData(2, -3, -1)]
        [InlineData(2, -2, 0)]
        public void AddTest2(int a1, int a2, int s)
        {
            Assert.Equal(s, Arithmetic.Add(a1, a2));
        }


You can see that AddTest2 will be called once for each [InlineData] with the specified set of parameters. This is a very powerful feature. The result is...


If we break the Add method by multiplying instead of adding, we can see the value of multiple tests with different parameters because some of the tests pass and some do not.

            return a1 * a2;






PostSharp Logging

Yet another pattern we manually (and badly) code is logging. Everyone wants to do it differently. It shouldn't be like that. PostSharp makes consistent logging a breeze. I'm going to add console logging to the program I wrote for my last blog post.

There are several ways to do this. I like the idea of setting up logging at the solution level in the postsharp.config file. Add this under the <Project> node.


  <Multicast>
    <When Condition="{has-plugin('PostSharp.Patterns.Diagnostics')}">
      <LogAttribute xmlns="clr-namespace:PostSharp.Patterns.Diagnostics;assembly:PostSharp.Patterns.Diagnostics" />
    </When>
  </Multicast>


Use NuGet to install PostSharp.Patterns.Diagnostics into your project.

Now find your startup class. For a console project, this is Main in Program.cs by default. Add a reference to PostSharp.Patterns.Diagnostics and instantiate the logging backend. For console logging this is

LoggingServices.DefaultBackend = new PostSharp.Patterns.Diagnostics.Backends.Console.ConsoleLoggingBackend();

So Main looks more like this...

        static void Main(string[] args)
        {
            LoggingServices.DefaultBackend = new PostSharp.Patterns.Diagnostics.Backends.Console.ConsoleLoggingBackend();

            Document document = new Document() { ID = 1, Number = "A23" };
.
.
.
        }


Refer to the excellent PostSharp documentation for the packages and syntax required for other logging backends.

Now run the project and look at the output. Pretty sweet, eh? Of course, it's highly customizable.



You can adjust the verbosity at any time. For example, if I want to stop seeing information messages once the Document has been instantiated I could insert this line after the Document is instantiated.

Document document = new Document() { ID = 1, Number = "A23" };
LoggingServices.DefaultBackend.CurrentContextLocalConfiguration.Verbosity.SetMinimalLevel(LogLevel.Warning);





PostSharp [Aggregatable]

One of the things we have to work hard at is representing Parent/Child relationships in object trees. For example, we have documents and each document has a collection of details. We often need to hold a reference to the parent in each child. This allows us to pass the child object to a method and have the method browse to the parent. Postsharp allows us to do this easily.

Start a new console application in Visual Studio. I'm using 2019 with C#. Use the Nuget package manager to add PostSharp.Patterns.Model and installl it in your project. If you have a license, add the postsharp.config file now.


Now let's mock up some classes with a parent/child relationship in Program.cs

using PostSharp.Patterns.Collections;
using PostSharp.Patterns.Model;
using System;
using System.Collections.Generic;

namespace PostSharp2
{
    class Program
    {
        [Aggregatable]
        public class Document
        {
            public int ID { get; set; }
            public string Number { get; set; }

            [Child]
            public IList<Detail> Details { get; set; }

            public Document()
            {
                Details = new AdvisableCollection<Detail>();
            }
        }

        [Aggregatable]
        public class Detail
        {
            [Parent]
            public Document Document { get; set; }

            public int ID { get; set; }
            public string AccountNumber { get; set; }
            public decimal Amount { get; set; }
        }

        static void Main(string[] args)
        {
        }
    }
}
 .
We add the [Aggregatable] attribute to the parent and detail classes to indicate they take part in an aggregatable relationship. The detail collection is a new type of collection called AdvisableCollection. The Detail class has a Parent placeholder. We do not populate it - the AdvisableCollection's Add method does that.

Let's add some code to Main that populates and then interrogates our objects.

        static void Main(string[] args)
        {
            Document document = new Document() { ID = 1, Number = "A23" };
            Detail detail = new Detail() { ID = 2, AccountNumber = "XJ3-F", Amount = 100 };
            Console.WriteLine(string.Format("Before Add: Parent={0}", detail.Document?.Number));

            document.Details.Add(detail);
            Console.WriteLine(string.Format("After Add: Parent={0}", detail.Document?.Number));

        }

When we run this code we see that before we add the detail to the document's detail collection the detail's Parent is null, but afterwards it contains a reference to the document.