Unit Testing and Dieting

Before you learn to write any code, you need to learn how to test it. But surprisingly, I find people don’t test their code as much as they talk about TDD (Test Driven Development) or BDD (Behavior Driven Development). Everyone knows the importance of test, and can talk about length on the benefit of TDD or BDD, but below the surface, when you open the solution and peek into it, you don’t find much testing done. I have seen, projects after projects which don’t have enough unit testing. TDD is very similar to dieting, everyone knows about them, every one talk about them, but most of people don’t follows proper diet.

Here are the couple of reason, I believe people don’t write enough unit test:

Developer often underestimate time needed to code.

Developer has tendency to over commit, and most often under-estimate time needed to complete a task. When they are under time pressure, the first thing they cut is “unit test”. In fact, it should be other way around. But then we do many stupid things like this, for example when we are stressed out or under time pressure,  first thing we do is to cut our exercise and Gym.

Not able to choose right testing framework.

A wrong framework makes writing unit test hard, and generally has other side effects which slows down the speed of development.

Code is not written properly and not following SOLID principle of object oriented design pattern.

In fact, the inability to write Unit test often a sign that you are violating SOLID,  specifically ‘Single responsibility principle’ and ‘dependency inversion’ principle of SOLID. If you don’t know what is SOLID then stop here learn about it and then come back, it is more important than this post :)

Good developers often use this trick for the code review. When you look at a piece of code, you should think how this code can be unit tested. If you cannot come up with the unit test easily then most probably this code need some re-factoring. Sure, there are cases when this may not hold true, but more often not it will be true.

Any good unit test must have these properties:

  • Atomic – Unit test should be atomic. This means unit test should test only ONE piece of responsibility. Going back to SOLID principle. Make sure, you strictly adhere to SOLID. A unit test should never test more than one feature.
  • Fast – Unit test should run under millisecond. Anything coming near second is indicating some problem in your chosen testing framework or your testing code.
  • Deterministic – Test should always either fail or pass.
  • Order independent – No specific order is needed to run the unit test.
  • Repeatable – Test should be repeatable. You can run them multiple time and they don’t disturb the system under test. There should not be any side effect of Unit test. If you are leaving side effects, then most probably you are confusing Unit test with end to end test.
  • Easy to Setup – It should be easy to run. If you have big ceremony around running test, install bunch of things on the box, need too much other dependency to run a unit test then you have chosen a wrong frame work or you have not written a proper unit test.

While writing unit test, the biggest complexity arises when objects have other dependent objects. To remove the dependent objects from unit test, you need to use the Test doubles, fakes and mock.

One naïve option is to implement a hand roll mock object. It is very expensive and time-consuming. Not recommended! The complexity will grows as the code base complexity will grow. It will add more code to be maintained. These mock object will become brittle with time, as the production code will change, any change in the interface will call for mock object change too.

One of the other option is to use Moq, a mocking framework for .NET. It is used to isolate your class under test from its dependencies and ensure that the proper methods on the dependent objects are being called. For more information on mocking you may want to look at the Wikipedia article on Mock Objects. Other mocking frameworks (for .NET) include TypeMock, RhinoMocks, nMock, .etc. But in this article we will keep our focus on Moq.

You can create a mock of only Interfaces. This is the first enforcer of your good habit that always design by contract.

To create a mock object using Moq, just simply use an interface as following:

 Mock mockCustomer = new Mock();

Once you have the mock object, you can manipulate its methods, properties, events etc.

One of the main pattern to know of Moq is AAA. Arrange, Act and Assert.

Arrange

  • Create a mock object
  • Pass the mock to SUT (system under Test)

Act

  • Execute the SUT (System under Test)

Assert

  • Verify SUT’s interaction with the mock object.
  • All your test should be written in this pattern.

To get started with Moq, open Visual studio and click on tools -> manage NuGet packages. Search for Moq and install it.

Moq

When you are in NuGet package explorer, get NBehave also. This provide some nice functionality. One example is you can write code like this

 age.ShouldEqual(35); // where age is a int variable

Before we understand how Moq works, let’s setup some code. (Disclaimer: This code is just to show how Moq works, this is not about designing a production sales system, ya, ya there can be better return types, methods can be changed into property etc, etc ..you get the point, this is just to quickly explain Moq).

using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HelloMOQ
{
    class Program
    {
        static void Main(string[] args)
        {

        }
    }

    public interface ICustomer
    {
        string GetName();
        int GetBalance();
        int SetAge(int i);
        string Country {get; set;}
    }

    public interface IProduct
    {
        int Qty { get; set; }
        string Name { get; set; }
        int GetPrice(out float productPrice);
        Product Create(string s);
        event EventHandler NotifyWarehouse;
    }

    public interface IOrder
    {
        bool SaveOrder (ICustomer cust, IProduct prod) ;
        void PremiumCustomer(ICustomer cust);
        string Status { get; set; }
    }

    public class Order:IOrder
    {
        string _status = "empty";

        public string Status
        {
            get { return _status; }
            set { _status = value; }
        }

        public bool SaveOrder(ICustomer cust, IProduct prod)
        {
            if (prod.Qty > 100)
            {

                prod.NotifyWarehouse += prod_NotifyWarehouse;
                PremiumCustomer(cust);
            }

            return true;
        }

        void prod_NotifyWarehouse(object sender, string e)
        {
           Status = e;
        }

        public void PremiumCustomer(ICustomer cust)
        {
            cust.GetName();
            Console.WriteLine("PremiumCustomer");
        }
    }

    public class Customer : ICustomer
    {

        string _country;
        string _firstName;
        string _secondName;
        int _age;

        public string Country
        {
            get { return _country; }
            set { _country = value; }
        }

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string SecondName
        {
            get { return _secondName; }
            set { _secondName = value; }
        }

        public string GetName()
        {
            return _firstName + _secondName;
        }

        public string SetName(string name1)
        {
            _firstName = name1;
            return GetName();
        }

        public int GetBalance()
        {
            throw new NotImplementedException();
        }

       public int SetAge(int i)
       {
           _age = i;
           return _age;
       }
    }

    public class Product : IProduct
    {
        string _name;
        int _qty;

        public int Qty {
            get { return _qty;}
            set { _qty = value;}
        }

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public Product(string name)
        {
            _name = name;
        }

        public Product Create(string name)
        {
            return new Product(name);
        }

        public int GetPrice(out float productPrice)
        {
            throw new NotImplementedException();
        }

        public event EventHandler NotifyWarehouse ;
    }
}

Here is a code, to test if a function is never called. As we see above in the defination of ‘SaveOrder’ that the  ‘PremiumCustomer’ is only called when the Qty is above 100, so in the below code we made sure that Moq returns 99 (Highlighted line).

[TestMethod]
 public void Test_Function_Never_Called()
 {
   //Arrange
   Mock mockCustomer = new Mock();
   Mock mockProduct = new Mock();
   //seting up the expectation - That GetName of Customer should be called.
   mockCustomer.Setup(x => x.GetName());
   //This expectation makes sure that PremiumCustomer will never be called
   mockProduct.Setup(x => x.Qty).Returns(99);

   Order order = new Order();

   //Act
   order.SaveOrder(mockCustomer.Object, mockProduct.Object);

   //Assert - verify
   mockCustomer.Verify(x => x.GetName(), Times.Never());
 }

Counter to above code, the below code sets up the mock as such that PremiumCustomer function WILL be called.

 [TestMethod]
 public void Test_A_Function_must_be_Called_Atleast_OneTime()
 {
   //Arrange
   Mock mockCustomer = new Mock();
   Mock mockProduct = new Mock();
   //seting up the expectation - That GetName of Customer should be called.
   mockCustomer.Setup(x => x.GetName());
   //This expectation makes sure that PremiumCustomer will be called.
   mockProduct.Setup(x => x.Qty).Returns(101);
   Order order = new Order();

  //Act
  order.SaveOrder(mockCustomer.Object, mockProduct.Object);

  //Assert - verify
  mockCustomer.Verify(x => x.GetName(), Times.AtLeastOnce());
}

If you will comment the above line, where you setup mockCustomer

 mockCustomer.Setup(x => x.GetName());

Your verify will throw error, as mock is not setup with the expectation.

So in short, Moq works by  (ARRANGE) first setting up your mock, setting up return values etc. And then interact with the system (ACT). In last, (ASSERT) verify the results.

Just to have more clear understanding of Moq, you can see the below code will work, even if GetBalance() method is not implemented on Customer. Because in setup phase we setup MockCustomer that he should return 5555 when GetBalance() is called. So Moq takes care of everything for you and nowhere in the code the real Customer will be called.

 //Arrage
 Mock mockCustomer = new Mock();
 mockCustomer.Setup(x => x.GetName()).Returns("AFakeCustomerName");
 mockCustomer.Setup (x => x.GetBalance()).Returns(5555);
 //Act
 var custName = mockCustomer.Object.GetName();
 var balance = mockCustomer.Object.GetBalance();

//Assert - verify
 custName.ShouldEqual("AFakeCustomerName");
 balance.ShouldEqual(5555);

Moq creates a complete replica of original object, and with setup you setup its method and properties to return expected results. In the ACT section, you will often call the real objects, we call it system under test (SUT). However, this is also possible that you can pass real objects to mock objects.

Loose and Strict Mocking
In strict mode, moq raises exception for anything on a mock object that does not have an declared expectation. You must have a setup for every method, property etc.
In loose mode, no exception is raised. Returns default values when no expectation is explicitly declared. This is the default behavior.
Setting up strict mode

Mock mockCustomer = new Mock(MockBehavior.Strict);

Here are some more example of Moq

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using HelloMOQ;
using NBehave.Spec.MSTest;

namespace UnitTest
{
    [TestClass]
    public class UnitTest1
    {

        [TestMethod]
        public  void Test_Function_Never_Called()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            Mock mockProduct = new Mock();
            //seting up the expectation - That GetName of Customer should be called.
            mockCustomer.Setup(x => x.GetName());
            //This expectation makes sure that PremiumCustomer will never be called
            mockProduct.Setup(x => x.Qty).Returns(99);

            Order order = new Order();

            //Act
            order.SaveOrder(mockCustomer.Object, mockProduct.Object);

            //Assert - verify
            mockCustomer.Verify(x => x.GetName(), Times.Never());
        }

        [TestMethod]
        public void Using_Mock_Repository()
        {
            //Arrange
            var mockFactory = new MockRepository(MockBehavior.Loose)
                        { DefaultValue = Moq.DefaultValue.Mock };

            Mock mockCustomer = mockFactory.Create();
            Mock mockProduct = mockFactory.Create();
            //seting up the expectation - That GetName of Customer should be called.
            mockCustomer.Setup(x => x.GetName());
            //This expectation makes sure that PremiumCustomer will never be called
            mockProduct.Setup(x => x.Qty).Returns(199);

            Order order = new Order();

            //Act
            order.SaveOrder(mockCustomer.Object, mockProduct.Object);

            //Assert - verify
            mockCustomer.Verify(x => x.GetName(), Times.AtLeastOnce());
            mockFactory.VerifyAll();
        }

        [TestMethod]
        public void Test_A_Function_must_be_Called_Atleast_OneTime()
        {

            //Arrange
            Mock mockCustomer = new Mock();
            Mock mockProduct = new Mock();
            //seting up the expectation - That GetName of Customer should be called.
            mockCustomer.Setup(x => x.GetName());
            //This expectation makes sure that PremiumCustomer will not be called.
            mockProduct.Setup(x => x.Qty).Returns(101);

            Order order = new Order();

            //Act
            order.SaveOrder(mockCustomer.Object, mockProduct.Object);

            //Assert - verify
            mockCustomer.Verify(x => x.GetName(), Times.AtLeastOnce());

        }

        [TestMethod]
        public void Method_Returning_A_Fake_Customer()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            mockCustomer.Setup(x => x.GetName()).Returns("AFakeCustomerName");
            mockCustomer.Setup (x => x.GetBalance()).Returns(5555);
            //Act
            var custName = mockCustomer.Object.GetName();
            var balance = mockCustomer.Object.GetBalance();

            //Assert - verify
            custName.ShouldEqual("AFakeCustomerName");
            balance.ShouldEqual(5555);
        }

        [TestMethod]
        public void Method_Throws_Exception()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            mockCustomer.Setup(x => x.GetBalance()).Throws(new InvalidOperationException());

            ////Act

            //Assert - verify
            typeof(InvalidOperationException).ShouldBeThrownBy(
             () => mockCustomer.Object.GetBalance());
        }

        [TestMethod]
        public void Test_Arguments_Value_In_Function()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            int age = 0;
            //When SetAge is set, a callback is called which will in turn set age variable to the parameter value
            //which is pased to SetAge
            mockCustomer.Setup(x => x.SetAge(It.IsAny())).Callback((int a) => { age = a; });

            //Act
            mockCustomer.Object.SetAge(35);

            //Assert - verify
            age.ShouldEqual(35);
        }

        [TestMethod]
        public void Set_A_Property()
        {
            //Arrange
            Mock mockCustomer = new Mock();

            //Act
            mockCustomer.Object.Country = "USA";
            //Assert - verify

            mockCustomer.VerifySet( x => x.Country = "USA");
        }

        [TestMethod]
        public void Set_A_Property2()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            mockCustomer.SetupProperty(x => x.Country, "USA");

            //Act

            //Assert - verify
            mockCustomer.Object.Country.ShouldEqual("USA");

        }

        [TestMethod]
        public void Set_A_Property3()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            mockCustomer.Setup(x => x.Country).Returns("USA");

            //Act

            //Assert - verify

            mockCustomer.Object.Country.ShouldEqual("USA");

        }

        [TestMethod]
        public void Verifying_if_a_getter_is_called()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            mockCustomer.Setup(x => x.Country).Returns("USA");

            //Act
            var country = mockCustomer.Object.Country;

            //Assert - verify
            mockCustomer.VerifyGet(x => x.Country);

        }

        [TestMethod]
        public void Force_A_Value_On_A_Out_Parameter()
        {
            //Arrange
            Mock mockProduct = new Mock();
            float pp = 2.34F;
            mockProduct.Setup(x => x.GetPrice (out pp));

            ////Act
            float f;
            mockProduct.Object.GetPrice(out f);

            //Assert - verify
            f.ShouldEqual(pp);
        }

        [TestMethod]
        public void Make_Sure_A_Function_Was_Called()
        {
            //Arrange
            Mock mockProduct = new Mock();
            Mock mockCustomer = new Mock();

            //Act
            Order order = new Order();
            order.SaveOrder(mockCustomer.Object, mockProduct.Object); //Put system under test

            //Assert - verify
            mockProduct.Verify(x => x.Qty, Times.AtLeastOnce);
        }

        [TestMethod]
        public void Calling_function_With_Different_Parameter_For_Different_Return_Values()
        {
            //Arrange

            Mock mockProduct = new Mock();
            int i = 0;
            mockProduct.Setup(x => x.Create(It.IsAny()))
                .Returns(() => new Product((i++).ToString()));

            //Act

            //Assert - verify
            mockProduct.Object.Create("xx").Name.CompareTo("0").ShouldEqual(0);
            mockProduct.Object.Create("xx").Name.CompareTo("1").ShouldEqual(0);

        }

        [TestMethod]
        public void Raising_an_event()
        {
            //Arrange
            Mock mockCustomer = new Mock();
            Mock mockProduct = new Mock();
            //seting up the expectation - That GetName of Customer should be called.

            mockProduct.Setup(x => x.Qty).Returns(199);
            Order order = new Order();

            //Act
            order.SaveOrder(mockCustomer.Object, mockProduct.Object); //Must call SaveOrder which wires the event for Product

            //raise the event
            mockProduct.Raise(x => x.NotifyWarehouse += null
                ,null //Sender object to the event
                ,"WAREHOUSE_NOTIFIED"  //e argument to the event
                );

            //Assert - verify
            order.Status.ShouldEqual("WAREHOUSE_NOTIFIED");
        }

    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s