Wednesday, August 9, 2017

Test setup readability in C# 7

How do you set up the state and the data that you need in tests? Say that you are writing a view class for a domain object. In addition, you need to set up a mock environment that you need to assert against in order to make sure that the view is making the correct calls to environment (which might be a graphics card, a web service or a UI frontend:



  class DomainObject
  {
    ...
  }

  class View
  {    
    ...
  }

  class MockEnvironment : IEnvironment
  {    
    ...
  }



With NUnit, you can decorate setup methods with the [SetUp] or [SetUpFixture] attribute. These methods will be run before the tests or test class, allowing you to create data and set this as fields in the test class. A test class might look like this:




  public class Test
  {
    private DomainObject _domainObject;
    private View _view;
    private MockEnvironment _environment;

    [SetUp]
    public void CreateStuff()
    {
      // Create and initialize _domainObject, _view and _environment  
    }

    [Test]
    public void SomeMethod_Always_EverythingWorks()
    {
      // Test stuff and assert, using the class fields
    }
  }



However, that will cause issues if the tests are run in parallel. Actually, some test frameworks like xUnit has no support for setup methods because of this.

A better approach is to use maker methods that create the data with helper methods:




    [Test]
    public void SomeMethod_Always_EverythingWorks()
    {
      var environment = MakeEnvironment(some parameters);

      var domainObject = MakeDomainObject();
      var view = MakeView(domainObject, environment);

      // Do some more initialization

      // Implement test
    }

    private MockEnvironment MakeEnvironment()
    {
      ...
    }

    private DomainObject MakeDomainObject()
    {
      ...
    }

    private View MakeView(DomainObject domainObject, IEnvironment environment)
    {
      ...
    }


This quickly becomes messy if multiple objects are created. If they depend on each other, it becomes even more messy.

C#7  has a new feature that is very handy for this scenario: return tuples! With return tuples, you can create and initialise everything in one reusable method. Everything is still type safe and very readable:



    [Test]
    public void SomeMethod_Always_EverythingWorks()
    {
      var (view, domainObject, environment) = MakeViewAndStuff();

      // Implement test
    }

    private (View view, DomainObject domainObject, MockEnvironment environment) MakeViewAndStuff()
    {
      var environment = new MockEnvironment();
      var domainObject = new DomainObject();
      var view = new View(domainObject, environment);

      // Do more initialization

      return (view, domainObject, environment);
    }



Various tests in the test class may or may not reference the objects that are returned. For readability, simply assign the ignored return variables to a dummy underscore variable if you don't need them in the tests:



    [Test]
    public void SomeOtherMethod_Always_EverythingWorks()
    {
      var (view, _, _) = MakeViewAndStuff();

      // Implement test. No need to use the domain object and environment in this test
    }


Clever, huh?



Friday, August 4, 2017

Twitter

Just a small update from me... I am now on Twitter!

Well, I have had a Twitter account for years, but I never really used it. From now on, I'll post updates on Twitter whenever I write a new blogpost or stumble upon something which is related to test-driven development. Or other aspects related to software development. Or maybe even about boats!

https://twitter.com/jahnotto

@jahnotto