TDD and Unit Testing with Moq

In my last post, I mentioned that I've been using TDD and mocking quite a bit this year, and I've fallen in love. It's something I'm very keen on continuing with, as I've now seen that the benefits outweigh the extra time needed to develop your tests.

I've written my fair share of stand alone unit and integration tests throughout the years, but until recently I've not used object mocking or stubbing. I've never really appreciated what tests are, and what benefits they give me as a developer, other than they let me write a quick bit of throw-away code that enables me to test the functionality of a class or a method. Only now - 16 years in to my career - do I realise how critical unit tests are to an application, especially in enterprise level systems that have a lot of code reuse and crossover. Not only do well written tests test the functionality of your code base, they also allow you to modify anything with complete confidence that your changes aren't going to affect anything else in the software.

So, on to the wonderful world Test Driven Development (TDD). Shunned by many because of the extra time needed to implement it properly, and ultimately due to the perception that TDD is "doing development backwards". The fact of the matter is, the process of writing a test and making sure it fails before you even write a line of implementation code, proves that you haven't written the test incorrectly in the first place, or written a test that'll pass regardless of your implementation. It's also self documenting, and forces modular, well-written, and concise code - as long as you follow the TDD guidelines and principals. Probably most importantly, it forces developers to think about what they're writing; there's little point in writing a test that confirms that a + b = c if you haven't figured out what c is going to be used for.

Skipping merrily down the street hand in hand with TDD is mocking. I've tried to get my head around it in the past, but - as with TDD - I've never really understood its use, so I've never made time to learn it. Why would I want to mock an object up in my tests when I need to test real code? Why would I write a test against a mocked object? Both silly questions in hindsight, and both easily answerable if you consider that you don't actually test mocked objects - they merely give you the ability to ignore any logic contained in your external dependencies, letting you concentrate on one particular function block, or "unit" of code.

I'm gonna delve in to this a little deeper, in the vain hope that I can get a few people on board with TDD. Consider this simplified real world scenario; say you want to test if pressing the power button on your TV remote control will issue your TV with a "turn on" signal, and power it on. Now, consider that in order for this test to work, the TV must be plugged in and supplied with electricity, be in "stand by" mode, and the remote must have sufficiently powered batteries installed. All these variables make the original requirement hard to reliably test - how I can accurately and consistently test if the remote sends the "turn on" signal if the batteries might be dead, or if the TV will receive the signal when someone has turned the TV off at the wall, or a fuse has blown? My test will fail if one or more of the dependencies don't function as I expect them to, meaning I could spend a while trying to fix something that isn't even broken in the first place.

Suddenly you're presented with a plethora of things that can go wrong; if we were to run this test without mocking up the dependencies, we would be performing an integration test rather than a unit test. Integration tests are still important, but tests at the unit level are extremely useful to us. They confirm that - even after modification - one particular part of the software still functions as it was originally intended to, and our modifications haven't changed that. Further to this, mocking allows us to replace the batteries, electricity, and stand-by mode dependencies with rock-solid, predictable implementations that will always function in the same way.

Let's put this remote / TV scenario in to a code implementation. I've simplified things for clarity - I wouldn't normally pass around "magic strings", and I probably wouldn't design it this way if it was going in to production, but I think it makes things a bit clearer for the purposes of an example. I'm using MSTest and Moq in my examples, but the same can be achieved with any testing / mocking frameworks.

In TDD we write tests before code, so let's write one that checks to see if the TV is receiving signals from the remote (we're using text to represent the remote codes that a TV might receive):

        [TestMethod]
        public void DoesTVReceiveSignalFromRemote() {
            // Arrange
            // Create a mock TV - we don't really care what the TV does,
            // we just need to know that the remote successfully sends a signal
            var tv = new Mock<ITelevision>();

            // Create a concrete RemoteControl instance to test, passing in the 
            // TV via constructor injection.
            var remote = new RemoteControl(tv.Object);

            // Act
            // Have the remote send a signal
            remote.Send("foobar");

            // Assert
            // Assert that the TV received a signal with the text sent with the Send() method
            tv.Verify(x => x.ReceiveSignal(It.Is<string>(s => s == "foobar")), Times.Once());
        }

Obviously, because the ITelevision interface and the RemoteControl class don't exist, you won't even be able to compile the code, but this gives you a starting point. If I'm honest, I usually implement the very basics of my classes and interfaces before I write any tests (throwing a NotImplementedExceptions in any method implementations), but only because I find trying to build a test that's broken at compile-time a little bit pointless. The point of writing tests first is so you can confirm that your test is well written and concise. It also makes sure that your test is correct - if your test passes straight away, you've either already implemented the code you're testing (which means you've got a duplicate test somewhere), or your test has been written badly.

So, in the above test I'm passing a mocked TV object in to the RemoteControl as a dependency (which does nothing at all - it's just satisfying the constructor requirement for an instantiated class), invoking the RemoteControl's Send() method (passing the string "foobar"), then checking if the TV's ReceiveSignal() method is invoked with the same string. This test tells us that the RemoteControl's Send() method requires the invocation of the TV's ReceiveSignal() method as part of its functionality, and that the signal that's received must be the same as the one that's sent by the remote. The signal "foobar" is arbitrary - we're not testing the validity of the signal, we're just testing that a signal can be received by the TV.

One of the main principals of TDD is to write as little code as possible in order to make your test pass. This ensures you have clear, maintainable code, and have tests for as much functionality as possible. Here's the minimal implementation for the above test:

    public interface ITelevision {    
        void ReceiveSignal(string signal);
    }

    public class RemoteControl {
        private readonly ITelevision pairedTV;

        public RemoteControl(ITelevision tv) {
            pairedTV = tv;
        }
        public void Send(string signal) {
            pairedTV.ReceiveSignal(signal);
        }
    }

The test now passes. Notice that there's no implementation of ITelevision; as I mentioned earlier, I don't care what the television does at this stage, because I'm just testing that the RemoteControl.Send() method physically sends a message to the TV it's been paired with. Let's go a little deeper.

Imagine that I wanted to save the remote's battery consumption by detecting if the TV has electricity flowing to it before sending an "on" request. Ignoring the fact that the technicalities are extremely questionable, I could check this bit of logic by creating a new test:

        [TestMethod]
        public void RemoteDoesNotSendSignalIfTVIsTurnedOff() {
            // Arrange
            var tv = new Mock<ITelevision>();
            // Setup the TV mock so it is turned off
            tv.Setup(x => x.HasElectricalPower)
              .Returns(false);

            var remote = new RemoteControl(tv.Object);

            // Act
            // Have the remote invoke the Send() method - the data sent
            // does not matter (we are not testing the data)
            remote.Send(string.Empty);

            // Assert
            // Assert that the TV implementation does not invoke ReceiveSignal() at all.
            tv.Verify(x => x.ReceiveSignal(It.IsAny<string>()), Times.Never());
        }

I've now introduced the "HasElectricalPower" property to the ITelevision interface, and hard-wired the mock to return "false" in every circumstance. I'm then sending an empty string to the RemoteControl.Send() method, as the data I'm sending is even less important than in our first test; we're not testing the signal's value at all here, so we don't need to populate it; you could just as easily leave it as "foobar", or any other random string. Last but not least, I've changed the assertion to make sure that the ITelevision.ReceiveSignal() method is never invoked. Notice that I've also changed the matching condition (It.IsAny<T>()) so it will accept any string whatsoever.

This test tells us that the ITelevision.HasElectricalPower property must be "true" before a signal can be sent. To make this test pass with the minimal amount of code, add the following highlighted code to your implementation:

    public interface ITelevision {    
        void ReceiveSignal(string signal);
        bool HasElectricalPower { get; }
    }

    public class RemoteControl {
        private readonly ITelevision pairedTV;

        public RemoteControl(ITelevision tv) {
            pairedTV = tv;
        }
        public void Send(string signal) {
            if (pairedTV.HasElectricalPower) {
                pairedTV.ReceiveSignal(signal);
            }
        }
    }

The test now passes, but those who've been following along will notice that the first test we wrote now fails. This is because you've introduced an additional dependency that needs to be mocked; the ITelevision.HasElectricalPower() property. This now needs to return "true", because otherwise the first test we wrote will never pass. While we're at it, let's rename the test to reflect the changes we're making, and parameterise the signal text that we're sending (which is what I would do in my own tests to make them easier to maintain):

        [TestMethod]
        public void DoesTVReceiveSignalFromRemoteIfTurnedOn() {
            // Arrange
            // Create the signal text that we're going to send. We can use this variable 
            // to check the received text too.
            const string expectedSignal = "foobar";

            // Create a mock TV - we don't really care what the TV does,
            // we just need to know that the remote successfully sends a signal
            var tv = new Mock<ITelevision>();

            // Setup the TV mock to it's always turned on
            tv.Setup(x => x.HasElectricalPower)
              .Returns(true);

            // Create a concrete RemoteControl instance to test, passing in the 
            // TV via constructor injection.
            var remote = new RemoteControl(tv.Object);

            // Act
            // Have the remote send the signal
            remote.Send(expectedSignal);

            // Assert
            // Assert that the TV received the signal
            tv.Verify(x => x.ReceiveSignal(It.Is<string>(s => s == expectedSignal)), Times.Once());
        }

And there we have it - two working tests, and a better understanding of our code and why we should test it. Refactoring is part of the TDD design principals, and you can do it in confidence that you have tests that'll catch any slip ups you might make. I'm going to cover one more scenario before I finish.

Moving away from mocks; say we want to check that the TV actually turns on when we send the signal "on" to it. This time, I'm going to write three tests before I write the implementation:

        [TestMethod]
        public void TVIsOffByDefault() {
            // Arrange
            var tv = new Television();

            // Assert
            Assert.IsFalse(tv.IsOn);
        }

        [TestMethod]
        public void TVTurnsOnWhenTurnOnSignalIsReceived() {
            // Arrange
            var tv = new Television();

            // Act
            tv.ReceiveSignal("on");

            // Assert
            Assert.IsTrue(tv.IsOn);
        }

        [TestMethod]
        public void TVDoesNotTurnOnWhenOtherSignalIsReceived() {
            // Arrange
            var tv = new Television();

            // Act
            tv.ReceiveSignal("foobar");

            // Assert
            Assert.IsFalse(tv.IsOn);
        }

These tests now require me to implement a Television class. Notice that we're not using the RemoteControl class or mocking here - for the purposes of this example, the Television class doesn't have any external dependencies, so we're just checking that when it's first instantiated it's turned off. Also worth noting is that the Television implementation won't affect any of the previous tests, because they're testing with a mock implementation of the ITelevision interface, rather than the real implementation. In our production code, we would inject the Television class in to the RemoteControl implementation, similar to the way we injected the mock in our tests.

Once the Television class is created, the TVIsOffByDefault and TVDoesNotTurnOnWhenOtherSignalIsReceived tests will pass straight away (which is contrary to everything I've been saying about TDD), but what they do allow us to do is define that IsOn is always handled correctly when we finally get the TVTurnsOnWhenTurnOnSignalIsReceived implementation working. They allow us to check that you or any other developers don't do anything undesired, like implement a constructor that sets IsOn = true, or allow any signal at all to turn the TV on.

The TVTurnsOnWhenTurnOnSignalIsReceived checks that the receipt of the "on" signal turns the TV on, and the TVDoesNotTurnOnWhenOtherSignalIsReceived test makes sure that sending anything but "on" will not turn the TV on (well, not exactly, but it's as close as we're gonna get). The implementation for the Television class is below:

    public class Television : ITelevision {
        public bool HasElectricalPower { get; private set; }
        public bool IsOn { get; private set; }

        public void ReceiveSignal(string signal) {
            IsOn = signal.Equals("on");
        }
    }

So, there we have it - a beginners insight in to the world of TDD. As with anything to do with development, you can go absolutely crazy with mocking and testing, but I find it depends on the project. Unit tests should be simple and to the point, and a mocked object should only really ever implement the properties and methods that are required for the test you're writing to pass.

Popular posts from this blog

How I Learned to Lose Weight and Love Exercise (again)

AutoMapper: UseValue vs ResolveUsing vs MapFrom

GDPR: Application Password Security in 2018