Design, Test and Develop like it's heaven on Earth - Part 14

Published on 9/12/2019

In the previous part we made a start on our presentation layer. We put a mock data source in place for a blog post and gave ourselves the ability to iterate over changes to the user interface quickly. Now we need to move on to actually hooking up a real adaptor.

Testing another Adaptor

So in part 13 I alluded to the fact that the BlogServiceAdaptor in the web layer has knowledge of the BlogService in the domain layer, but of course I’m not able to mock out BlogService directly in order to test BlogServiceAdaptor. Well, I can. There are mocking frameworks out there that can impersonate a concrete implementation (with some limitations). However, I don’t want to mock out the concrete class anyway. This BlogServiceAdaptor implementation defines the boundary between the web layer and the domain layer. If it depends on the BlogService class directly, it is tightly coupled. And then the web layer is tightly coupled to the domain layer. That is not good.

Just plain old dependency injection

We fix this by defining an abstraction IBlogService in the domain abstractions library. This time though, we’re not really inverting the dependency. Because the domain layer is consumed by the web layer, introducing an abstraction for the BlogService class doesn’t really invert anything. It’s just an abstraction, and speaks more towards the contract between the presentation layer and domain layer, composition over inheritance perhaps, and the interface segregation principle. This is because, by being able to inject IBlogService into other implementations, we will be composing those implementations to also have blog functionality.

So lets define that interface, and move on to provide a test for the BlogServiceAdaptor implementation.

    public interface IBlogService
    {
        Task<Blog> Read(string title);
        Task Create(Blog blog);
        Task Publish(string title, IEnumerable<SyndicationText> syndicationTexts);
    }

This abstraction basically just defines the existing methods we already have in our implementation. Of course, we need to make sure that BlogService implements this interface.

    public class BlogService : IBlogService

And then the test.

        [TestMethod]
        public async Task GetBlog_Verify()
        {
            //arrange
            string title = "test-title";
            string content = "content";
            Blog blog = new Blog() { Title = title, Content = content };
            Mock<IBlogService> serviceMock = new Mock<IBlogService>();
            serviceMock.Setup(x => x.Read(title))
                .ReturnsAsync(blog);
            IBlogServiceAdaptor adaptor = new BlogServiceAdaptor(serviceMock.Object);

            //act
            Models.BlogView result = await adaptor.GetBlog(title);

            //assert
            serviceMock.Verify(x => x.Read(title));
            Assert.AreEqual(blog.Title, result.Title);
            Assert.AreEqual(blog.Content, result.Content);
        }

And then the implementation.

    public class BlogServiceAdaptor : IBlogServiceAdaptor
    {
        readonly IBlogService _service;

        public BlogServiceAdaptor(IBlogService service)
        {
            _service = service;
        }

        public async Task<BlogView> GetBlog(string title)
        {
            var blog = await _service.Read(title);
            return new BlogView()
            {
                Title = blog.Title,
                Content = blog.Content
            };
        }
    }

Naming is important

This passes the test, but while doing that I noticed something that is not very well aligned and doesn’t follow our project’s convention. Can you spot it?

During the initial planning and architecture phase of this project we talked about using the CRUD terms, and indeed in BlogService (and IBlogService) we use the method name Read. Yet, in our adaptor here in the web layer, I used the method name GetBlog, which calls Read. This is inconsistent, and makes the code difficult to follow for other developers. We need to address this, and I make use of the refactoring tools to do just that.

Concept: Mapping

Now we get to our first mapping code. In the implementation for BlogServiceAdaptor we create a new instance of BlogView, and assign the various property values over from Blog to BlogView. This code violates SOLID principles for the same reason that we moved the validation code out from the Create and Update methods on the BlogService implementation. The Read method should not be concerned about mapping, it should be concerned about orchestrating the reading of the blog and mapping of the blog.

We have a few options here. We can move it into a private method. That’s a fine solution. It’s not a lot of code to move. We can also create a constructor on BlogView and take in Blog and assign the properties there. This is less ideal, since the code for this class would then be dependent on the domain model. Also consider that, regardless of where we put this code that assigns the properties, because it’s code that we write we have to test it. Testing a handful of assignments is fine, but what if your model has twenty properties? That’s lines and lines of tedious code to test, code that you don’t have to test if you don’t write it. How do you not write it, but still get the mapping done? We use a specialized mapper package.

Conventions

My favourite mapper package is Automapper. There are others of course, so use the one that you prefer. Automapper works on convention, and understanding these convention targets is key to using the package effectively and not having to write code or tests. Convention-based code is also only possible if the whole team understands and follows the naming conventions. Using disparate naming everywhere in the code would completely negate the use of Automapper, and then you might as well not use it.

We start by defining the mapper configurations. There are two ways - set it up with dependency injection, or set it up with static accessors. In general I prefer the latter since mappings is integral and constant within the adaptor implementations. There is no need to ‘pick a mapper’ if your abstractions are well defined and strongly typed. Of course, there are cases where you would perhaps follow the strategy pattern and choose mappers, but this is not one of them.

    public static partial class Config
    {
        public static IMapper Mapper { get; private set; }
        static Config()
        {
            MapperConfiguration configuration = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<BlogConfig>();
            });

            Mapper = configuration.CreateMapper();
        }
    }

And in a separate file, grouped by blog, we define this BlogConfig profile.

    public static partial class Config
    {
        public class BlogConfig : Profile
        {
            public BlogConfig()
            {
                CreateMap<Blog, BlogView>();
            }
        }
    }

The CreateMap method call there is the important part. That is what defines and sets up the mapping. And if you have the same property or field names in both types, you don’t have to write any more code. But, there is a test we should write that checks the conventions for us.

        [TestMethod]
        public void ConfigTests_Valid()
        {
            Config.Mapper.ConfigurationProvider.AssertConfigurationIsValid();
        }

This test will throw an exception if there are properties on the target type (mapping to) that cannot be matched with anything on the source type (mapping from). Essentially, it helps us by checking that either all our properties or fields are matched through convention, or we supply overrides where there isn’t a match in convention.

Now that we have all that defined, we can rewrite our adaptor implementation method to the following.

        public async Task<BlogView> Read(string title)
        {
            Domain.Models.Blog blog = await _service.Read(title);
            return Config.Mapper.Map<BlogView>(blog);
        }

And I can remove the two asserts that I have in that unit test that checks the Title and Content for equality. I don’t need to assert this any more because I know that Automapper will take care of it.

Conversion

Now we get to the meaty bit of this adaptor, taking markdown and converting it to HTML. We’ll use another Nuget package for this, since parsing markdown and generating HTML is a non-trivial exercise that is beyond the scope of this series. The one that I know, and that supports the CommonMark “standard”, is Markdig. It has options to inject some non-standard elements so that you gain support for items like tables. I’ve never used a table in a blog post, but it’s nice to know that I will have that ability.

However, Markdig’s implementation is centered around static methods. The package doesn’t provide an abstraction, and so it’s not possible to inject it as a service as part of dependency injection, not unless you wrap your own abstraction around it. This makes it hard to test that our code correctly calls this package.

At the moment our mapper config is responsible for taking the content from the Blog domain model to the BlogView web model. These mapper configs are already statically defined and accessed, so I’m just going to carry on with a static method by providing a custom override for the Content property.

                CreateMap<Blog, BlogView>()
                    .ForMember(x => x.Content, opt => opt.MapFrom(blog => blog.Content.AsHtml()));

And this AsHtml() extension method is defined as

    public static class BlogMappers
    {
        public static string AsHtml(this string markdown)
        {
            var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
            return Markdown.ToHtml(markdown, pipeline);
        }
    }

Now we have two new pieces of code that we need to write tests for. The first is for the actual extension method.

        [TestMethod]
        public void AsHtml_Transformed()
        {
            //arrange
            string markdown = @"# Heading
Some paragraph here.

```
a quoted block
```

Another paragraph";

            //act
            string html = markdown.AsHtml();

            //assert
            Assert.AreEqual("<h1 id=\”heading\”>Heading</h1>\n<p>Some paragraph here.</p>\n<pre><code>a quoted block\n</code></pre>\n<p>Another paragraph</p>\n", html);
        }
    }

Because Markdig is used statically, we can’t test that we call it. We have to leverage off of the result of our method that something was called that produces markdown. This is the best that we can do in this scenario. The other is for the mapper config override.

        [TestMethod]
        public void BlogConfig_Content()
        {
            //arrange
            Blog blog = new Blog() { Content = "content" };

            //act
            BlogView result = Config.Mapper.Map<BlogView>(blog);

            //assert
            Assert.AreEqual("<p>content</p>\n", result.Content);
        }

These two tests together covers all the code we had to write in order to map the domain model to the web model, including the markdown engine.

But the tests wasn’t first?

I always strive to write the tests first. In fact, that is how I started part 3 and part of the premise of this blog series. But sometimes it’s not possible, or you don’t know enough to do that. In this case we were investigating Markdig, trying to see how we can access its functionality. Perhaps you do that outside of your project (like in LINQ pad) in which case you can write the tests first and then put the code back into your project. But sometimes you write the code to prove the concept in your project. There’s no point in rewriting it. You just need to tie down your functionality by proving the correctness with tests afterwards. This should only ever be the exception of course.

Wrapping up

I think we’re close to finalizing the presentation layer for serving a single blog entry. There is one thing I think we can still do to make it a bit better. In that extension were we call Markdig, we set up a pipeline using some default builder. This seems like unnecessary work that ideally should not be done every time someone requests a blog from my site. This pipeline probably doesn’t need to be instantiated every time, so we can make it a singleton.

    public static class BlogMappers
    {
        internal static MarkdownPipeline _pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();

        public static string AsHtml(this string markdown)
        {
            return Markdown.ToHtml(markdown, _pipeline);
        }
    }

Moving it to a static property means that it is instantiated whenever it is first referenced, and then just reused until the app is closed or the website is stopped.

Conclusion

This leaves us with the ability to load up a blog from a source using a title in the URL, and display that blog using a Blazor page that shows the title and the content. We have the ability to provide this blog in raw markdown format. We saw how all this now happens in the presentation layer, because these are all presentation concerns. We implemented our first mapper to prevent a dependency in the web model to the domain model, and so that we don’t write tedious code that we need to provide trivial assignment tests for.