Pages

Wednesday, August 31, 2016

Catching Up on Coding - TDD Part 3

I’ve been a software developer for twenty years, but am behind in some skills. This series chronicles my self-improvement.

It turns out that unit testing exercises a lot of coding methods for more maintainable code. Some methods involve deception, so who better to inspire us than our “Butch Cassidy/Sundance Kid” knockoffs from 70s show Alias Smith and Jones? The difference is, I’m not trying to “get out of this business.”

Previously on CUOC: TDD Part 2
Next Episode: Claims-Based Identity Part 1

 

Teaser

Unit testing benefits from mocking and Inversion of Control. I’d argue that it’s hard to effectively unit test without these techniques. Parts one and two of this TDD series used hand-crafted mocking and IoC. Today, we’ll redo some tests using 3rd party tools/frameworks.

Act 1 – Choosing the Tools

I researched what developers are recommending for mocking and IoC. Of course, opinions abound, but in the end I came to a few conclusions.

Mocking

  • No one recommends Rhino Mocks anymore. It appears to be a mostly dead project.
  • Moq is still used, but is falling out of favor.
  • Developers like NSubstitute and FakeItEasy.
  • NSubstitute has a more concise API. FakeItEasy is more natural language and less ambiguous.
  • My reaction to seeing them side-by-side is that I like FIE’s syntax more. I like tests that are immediately easy to understand.
  • But I’m a little put off by the verbosity, so I’ll try NSubstitute first.

References

C# Test Mocking Frameworks

NSubstitute: A Refreshingly Simple Mocking Framework for Unit Tests

Mocking: why we picked NSubstitute

Easy Mocking with FakeItEasy

IoC

  • Ninject is the most popular, and has the worst performance (though some developers argue that IoC container performance doesn’t matter).
  • Unity is supported (for now) by Microsoft, but people have complained about some limitations.
  • StructureMap, Autofac, and Simple Injector remain as the choices. Based on a few reviews—but mostly gut instinct—I’m going to use Simple Injector. (Autofac would be my second choice.)

References

IoC Container Benchmark – Performance comparison

Inversion of Control Containers Basic Comparison

IoC Battle in 2015 results: Using Ninject – think again!

Generic variance in DI containers

Simple Injector Migration Guide <-I love that they include how to migrate away from their product. Also serves as a coding comparison.

Act 2 – Making a Mockery of Testing

Install NSubstitute

  1. Install the package into the unit test project using NuGet.
  2. After installation, a nice readme.txt file opens. Read it.

Replace a Previous Test

In my previous tests, I created my own mock Registry wrappers from IRegistryWrapper and IRegistryMockWrapper. These are interfaces I created to allow Inversion of Control of the Registry. IRegistryWrapper looks like this

    public interface IRegistryWrapper
    {
        object GetValue(string keyName, string valueName, object defaultValue);
        void SetValue(string keyName, string valueName, object value, RegistryValueKind valueKind);
    }

the hand-crafted mock looks like this

    class RegistryMock : IRegistryWrapper
    {
        public object GetValue(string keyName, string valueName, object defaultValue)
        {
            //this may become cumbersome, but for now specify some return values
            switch (keyName)
            {
                case @"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent":
                    if (valueName == "Version") return defaultValue;
                    break;
            }
            throw new Exception("Invalid keyName");
        }

        public void SetValue(string keyName, string valueName, object value, RegistryValueKind valueKind)
        {
            throw new NotImplementedException();
        }
    }

here’s the unit test

    public class RegistryService_GetRegistryKeyValue_Should
    {
        IRegistryWrapper _registry = new RegistryMock();
        IRegistryKeyWrapper _registryKey = new RegistryKeyMock();

        [Fact]
        public void ReturnNullConstantTextIfValueIsMissing()
        {
            var service = new RegistryService(_registry, _registryKey);
            var result = service.GetRegistryKeyValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version");
            Assert.Equal(result, service.NoValue);
        }
    }

and finally, here's the RegistryService method that passes the test

        public string GetRegistryKeyValue(string keyName, string valueName)
        {
            try
            {
                //value will be null if no value found, and the default value if the key is missing. But we only care that there's no value.
                //Null can't be returned to the client. Don't return empty, since that's ambigious (did the key exist?).
                //Return a magic string.
                object value = _registry.GetValue(keyName, valueName, null); //guarantees null if no value or missing key.
                return value != null ? value.ToString() : NoValue;

            }
            catch (Exception ex)
            {
                return "Error attempting keyName '" + keyName + "' and valueName '" + valueName + "'\r\n" + ex.GetBaseException().Message;
            }
        }

All I’m going to do is refactor the test so that I use NSubstitute instead of my own mocks. I’ll need a mock for IRegistryKeyWrapper because the class expects it, but I won’t set any behavior on it.

Note: remember to add “using NSubstitute;”

In my hand-crafted mock, I specified return values based on the arguments to GetValue, and otherwise threw an exception. For my unit test, I need to do something similar. Here’s my first attempt, and it does indeed pass.

            
            var registry = Substitute.For<IRegistryWrapper>();
            var registryKey = Substitute.For<IRegistryKeyWrapper>();
            //Registry.GetValue will return null if the valueName doesn't exist.
            registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version", null).Returns(null);
            var service = new RegistryService(registry, registryKey);
            var result = service.GetRegistryKeyValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version");
            Assert.Equal(result, service.NoValue);

The problem is (or seems to be), if I change the substitute's argument "Version" to "Blamo", the test still passes. This is happening because given an unknown argument the method will still return null. I need to tell the substitute that it should only return null if it gets the right arguments.

Or do I?

No, I don’t. I’m attempting to give my mock too much behavior. I only want the mocked Registry to pretend that the value was missing and return null. Elsewhere I’ll test the behavior if the value exists. My hand-crafted mock was eventually going to give me trouble because it was doing too much and it wasn’t really behaving like the Registry class.

The Heart of Mocking

The method I’m testing happens to be  a good one for getting at the essence of mocking. RegistryService.GetValue is almost a pass-through to Registry.GetValue. But, it has its own behavior and is supposed to call Registry.GetValue.

As the developer and tester, I know three things:

  1. How external dependencies behave (Microsoft.Win32.Registry).
  2. How my test behaves.
  3. What’s in my method.

That last one is the problem. I know my method calls Registry.GetValue(), because I wrote it. But that’s a serious mental mistake when it comes to the test. I need to write my tests, whether before or after, as if I knew nothing about the method’s code. I should pretend the only things I know about the method are:

  1. The parameters it’s called with.
  2. The dependencies it has.
  3. The result when it’s called with particular values.

In short, my method is a black box. It could be empty, for all I know. That’s why TDD starts with an empty method, so that it fails first. If it succeeds when empty, that’s a problem. When my method has a dependency, I need to only mock up what that dependency will do if it’s called with the expected value.

But, I mustn’t assume the mocked dependency has been called at all.

So, here’s how my test should look.

            //Arrange
            var registry = Substitute.For<IRegistryWrapper>();
            var registryKey = Substitute.For<IRegistryKeyWrapper>();
            //Registry.GetValue will return null if the valueName doesn't exist.
            registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version", null).Returns(null);
            var service = new RegistryService(registry, registryKey);
            //Act
            var result = service.GetRegistryKeyValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version");
            //Assert
            //First, make sure the mocked Registry was called at least once. Otherwise, we might have a false positive.
            registry.Received().GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\EduAgent", "Version", null);
            //Now make sure we got back the expected value from our method.
            Assert.Equal(result, service.NoValue);

To sum up, some key lessons of unit testing are:

  1. Treat the method under test as a black box.
  2. Test behaviors of the method, not its dependencies.
  3. Verify mocked dependencies are called.

A mocking framework gives more value than just controlling return values. It lets you know that your dependencies are being used the way you expect.

Act 3 – Ouch! Injecting Dependencies

  1. Interfaces are used in production code to make it easier to inject dependencies.
  2. Mocking frameworks are used in unit testing code to control injected dependencies.
  3. IoC containers are used in production code to manage injected dependencies.

For the first two, see Act 2, above.

I don’t have  to use an IoC container. I can write code like this:

//pass the live registry wrappers
RegistryService service = new RegistryService(new RegistryWrapper(), new RegistryKeyWrapper());

I could also let my class have default dependencies. This might be OK, as long as the caller knows what to expect. It would be considered "convention over configuration."

    public class RegistryService
    {
        private IRegistryWrapper _registry;
        private IRegistryKeyWrapper _registryKey;

        public RegistryService()
        {
            _registry = new RegistryWrapper();
            _registryKey = new RegistryKeyWrapper();
        }
        public RegistryService(IRegistryWrapper registry, IRegistryKeyWrapper key)
        {
            _registry = registry;
            _registryKey = key;
        }
        ...
    }

    //usage, by default uses the Windows Registry.
    var service = new RegistryService();

For simple cases, using an IoC is overkill. In fact, I would say:

Don’t use an IoC container unless you have to.

When do you “have to”?

  • Where there are lots of dependencies.
  • When you have lots of nested dependencies.
  • When you want your dependency initialization to be in one place.

There are probably other reasons, but those will do for now.

Install Simple Injector

  1. Get it from NuGet, install to the project that’s injecting the dependencies.

Injection!

First, I prepare my RegistryService class for dependency injection.

    public class RegistryService
    {
        private IRegistryWrapper _registry;
        private IRegistryKeyWrapper _registryKey;

        public RegistryService(IRegistryWrapper registry, IRegistryKeyWrapper key)
        {
            _registry = registry;
            _registryKey = key;
        }

My Windows service, EduAgent, is calling the RegistryService, which is in a separate DLL. I want all my dependencies initialized once, in one place. The service’s Main method is the starting point. But, you know what, to make it easier for another developer, I’ll create a class called DependencyInjection, which we’ll call from Main. That makes it pretty clear where the dependencies are maintained, and how.

All we’re doing here is saying “if I request from ‘container’ a type of IRegistryWrapper, give me an instance of the class RegistryWrapper.”

    public static class DependencyInjection
    {
        public static Container container;
        public static void InitializeDependencies()
        {
            //create container
            container = new Container();
            //register interfaces
            container.Register<IRegistryWrapper, RegistryWrapper>();
            container.Register<IRegistryKeyWrapper, RegistryKeyWrapper>();
            //register concrete types
            container.Register<RegistryService>();
            //optionally verify
            container.Verify();
        }
    }

I initialize the dependencies.

    static class Program
    {
        /// 
        /// The main entry point for the application.
        /// 
        static void Main()
        {
            DependencyInjection.InitializeDependencies();
            ServiceBase[] ServicesToRun;
            ...

Notice that during initialization, I’m also registering the concrete type RegistryService. I’m doing this so that I can request an instance of RegistryService without passing any constructor arguments. The container knows what to pass in on my behalf.

   //pass the live registry wrappers, which are auto-wired by Simple Injector
   RegistryService service = DependencyInjection.container.GetInstance<RegistryService>();

This is the same as if I had done this:

   RegistryService service = new RegistryService(new RegistryWrapper(), new RegistryKeyWrapper());

But by using an IoC container, if I use a different (again, fictitious) registry provider, I only need to set it in one place: InitializeDependencies.

Tag

There’s a lot more to mocking and dependency injection than the above samples show. I ran into a pretty significant problem because my original IRegistryKeyWrapper included IDisposable, which plays hell with dependency injection.

Lesson: Sometimes these tools add complications. But other times, they reveal problems in your code.

On the subject of complications, one of the stars of Alias Smith and Jones killed himself during the first season. He suffered from depression and alcoholism. To this day, that makes me sad. Pete Duel, I miss what you could have done.

image

No comments:

Post a Comment