loading...

Getting started with Unit Testing and Moq - Part 4

kritner profile image Russ Hammett Originally published at kritner.blogspot.com on ・5 min read

Part 1

Part 2

Part 3

Part 4 you are here

In the previous post, we had setup our basic WCF project to play around with for unit testing. Since then, I have pulled out the WCF service reference in the console application, and placed it in our business project. Now that the business project has the WCF service reference, I have added a new class that handles the newing up of the WCF client.

Additionally, I have updated the console application to use the new business object wrapper of the WCF client.

Both of those classes look like this:

Program.cs

namespace RussUnitTestSample
{
  class Program
  {

      #region consts
      const string CONNECTION\_STRING = "Data Source=192.168.50.4,1515;Initial Catalog=MBES;Persist Security Info=True;Integrated Security=true;";
      #endregion consts

      #region Entry

      static void Main(string[] args)
      {
          GetNumbersAndAddThem obj = new GetNumbersAndAddThem(
              new DbGetSomeNumbers(new BaseDbConnection(CONNECTION\_STRING)),
              new NumberFunctions()
          );

          Console.WriteLine("\n");
          Console.WriteLine(obj.Execute());
          Console.WriteLine("\n");

          Business.WCF.Service1 service = new Business.WCF.Service1();

          Console.WriteLine("\n");
          Console.WriteLine("{0}", service.GetData(42));
          Console.WriteLine("\n");

      }

      #endregion Entry
  }

}

WCF.Service1

namespace RussUnitTestSample.Business.WCF
{

    /// <summary>
    /// Communication with the WCF Service1
    /// </summary>
    public class Service1
    {

        #region Private
        private ServiceReference1.Service1Client \_service;
        #endregion Private

        public Service1()
        {
            this.\_service = new ServiceReference1.Service1Client();
        }

        public string GetData(int value)
        {
            return this.\_service.GetData(value);
        }

    }
}

I moved the WCF service and newing up of that client from the console application to make it easier to unit test. We are still not at a point that WCF.Service1 can be unit tested, though the service itself can.

I’ve added a new RussUnitTestSample.WCF.Tests project to my solution, and added the following tests for my Service1.svc class (the implementation of IService1).

As a reminder the IService1.cs was defined as:

// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IService1
{

    [OperationContract]
    string GetData(int value);

    [OperationContract]
    CompositeType GetDataUsingDataContract(CompositeType composite);

    // TODO: Add your service operations here
}

I have added the following unit tests based on the implementation in

Service1.svc:

namespace RussUnitTestSample.Wcf.Tests
{

    /// <summary>
    /// Unit tests for service1
    /// </summary>
    [TestClass]
    [ExcludeFromCodeCoverage]
    public class Service1Tests
    {

        /// <summary>
        /// Get data works as expected with a positive number
        /// </summary>
        [TestMethod]
        public void Service1\_GetData\_PositiveNumber()
        {
            // Arrange
            Wcf.Service1 service = new Wcf.Service1();
            int num = 55;
            var expected = string.Format("You entered: {0}", num);

            // Act
            var result = service.GetData(num);

            // Assert
            Assert.AreEqual(expected, result);
        }

        /// <summary>
        /// Get data works as expected with a negative number
        /// </summary>
        [TestMethod]
        public void Service1\_GetData\_NegativeNumber()
        {
            // Arrange
            Wcf.Service1 service = new Wcf.Service1();
            int num = -42;
            var expected = string.Format("You entered: {0}", num);

            // Act
            var result = service.GetData(num);

            // Assert
            Assert.AreEqual(expected, result);
        }

        /// <summary>
        /// Get data works as expected with zero
        /// </summary>
        [TestMethod]
        public void Service1\_GetData\_Zero()
        {
            // Arrange
            Wcf.Service1 service = new Wcf.Service1();
            int num = 0;
            var expected = string.Format("You entered: {0}", num);

            // Act
            var result = service.GetData(num);

            // Assert
            Assert.AreEqual(expected, result);
        }

        /// <summary>
        /// An exception is thrown when the CompositeType is null
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Service1\_GetDataUsingDataContract\_ExceptionThrownWhenCompositeTypeNull()
        {
            // Arrange
            Wcf.Service1 service = new Service1();

            // Act
            var result = service.GetDataUsingDataContract(null);
        }

        /// <summary>
        /// When BoolValue is false, do not append "Suffix" to StringValue
        /// </summary>
        [TestMethod]
        public void Service1\_GetDataUsingDataContract\_CompositTypeBoolValueFalse\_DoNotAppendSuffix()
        {
            // Arrange
            Wcf.Service1 service = new Service1();
            string testString = "Test";
            CompositeType ct = new CompositeType()
            {
                BoolValue = false,
                StringValue = testString
            };

            // Act
            var result = service.GetDataUsingDataContract(ct);

            // Assert
            Assert.AreEqual(testString, result.StringValue);
        }

        /// <summary>
        /// When BoolValue is true, append "Suffix" to StringValue
        /// </summary>
        [TestMethod]
        public void Service1\_GetDataUsingDataContract\_CompositTypeBoolValueTrue\_AppendSuffix()
        {
            // Arrange
            Wcf.Service1 service = new Service1();
            string testString = "Test";
            CompositeType ct = new CompositeType()
            {
                BoolValue = true,
                StringValue = testString
            };

            var expected = testString + "Suffix";

            // Act
            var result = service.GetDataUsingDataContract(ct);

            // Assert
            Assert.AreEqual(expected, result.StringValue);
        }

    }
}

Code Coverage:

Taking a look at our code coverage, you can see that currently we have 100% coverage for our RussUnitTestSample.Wcf project, but our coverage of RussUnitTestSample.Business has gone from 100, to 54.21. This is expected of course, as we have added a Wcf Service reference, as well as a wrapper of the WCF client. I think we could technically unit test the Service Reference code, but it is auto generated, so I think I’m going to ignore it for now. Wonder if I can exclude it from Code Coverage.

So now let’s look into how to go about testing our Business.Wcf client wrapper.

WCF.Service1

namespace RussUnitTestSample.Business.WCF
{

    /// <summary>
    /// Communication with the WCF Service1
    /// </summary>
    public class Service1
    {

        #region Private
        private ServiceReference1.Service1Client \_service;
        #endregion Private

        public Service1()
        {
            this.\_service = new ServiceReference1.Service1Client();
        }

        public string GetData(int value)
        {
            return this.\_service.GetData(value);
        }

    }
}

As this class currently stands, we’re working with a Service1Client and not an interface, so it’s difficult to unit test. Let’s do a little refactoring. Instead of newing up the Service1Client, let’s take in the interface of said client. After updating our class looks like:

namespace RussUnitTestSample.Business.WCF
{

    /// <summary>
    /// Communication with the WCF Service1
    /// </summary>
    public class Service1
    {

        #region Private
        private IService1 \_service;
        #endregion Private

        #region ctor

        /// <summary>
        /// Constructor - new up IService1 with client
        /// </summary>
        public Service1()
        {
            this.\_service = new Service1Client();
        }

        /// <summary>
        /// Constructor - takes in implementation of IService1
        /// </summary>
        /// <param name="service">The IService1 implementation
        public Service1(IService1 service)
        {
            if (service == null)
                throw new ArgumentNullException(nameof(service));

            this.\_service = service;
        }

        #endregion ctor

        #region Public methods

        /// <summary>
        /// Call service GetData
        /// </summary>
        /// <param name="value">The value to pass to the WCF service
        /// <returns>The returned value from the WCF service call</returns>
        public string GetData(int value)
        {
            return this.\_service.GetData(value);
        }

        #endregion Public methods

    }
}

Now that we’re taking in an interface of the service, we can write some unit tests:

RussUnitTestSample.Business.Tests.Wcf.Service1Tests.cs

namespace RussUnitTestSample.Business.Tests.WCF
{

    /// <summary>
    /// Unit tests for Service1
    /// </summary>
    [TestClass]
    [ExcludeFromCodeCoverage]
    public class Service1Tests
    {

        #region Private
        private Mock<iservice1> \_service;
        #endregion Private

        #region Public methods
        /// <summary>
        /// initialize the mocks
        /// </summary>
        [TestInitialize]
        public void Setup()
        {
            this.\_service = new Mock<iservice1>();
        }

        /// <summary>
        /// Exception thrown when IService implementation is not provided
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void Service1\_NullIService1InConstructor\_ThrowsException()
        {
            // Arrange / Act
            Business.WCF.Service1 service = new Business.WCF.Service1(null);
        }

        /// <summary>
        /// Object properly constructed when implementation of IService1 provided
        /// </summary>
        [TestMethod]
        public void Service1\_ConstructorWithProvidedIService1\_NewsCorrectly()
        {
            // Arrange / Act
            Business.WCF.Service1 service = new Business.WCF.Service1(\_service.Object);

            // Assert
            Assert.IsInstanceOfType(service, typeof(Business.WCF.Service1));
        }

        /// <summary>
        /// Ensure that a string is returned from Service1 when calling GetData
        /// </summary>
        [TestMethod]
        public void Service1\_GetDataTest()
        {
            // Arrange
            this.\_service.Setup(s => s.GetData(It.IsAny<int>())).Returns("test");
            Business.WCF.Service1 service = new Business.WCF.Service1(\_service.Object);

            // Act
            var result = service.GetData(It.IsAny<int>());

            // Assert
            Assert.IsInstanceOfType(result, typeof(string));
        }

        #endregion Public methods

    }
}

And our new code coverage:

Now we’ve hit everything except the default constructor used for Service1. Guess I’ll have to figure out how to accomplish that later. Also I added a .runsettings file to exclude “Service Reference” folders from code coverage.

Latest code as of post:

https://github.com/Kritner/RussUnitTestSample/tree/b9c2f329adbc700688fb69943cc4b7b28ffd87c4

Posted on by:

kritner profile

Russ Hammett

@kritner

Dad, programmer, faux-chef, gamer, book reader

Discussion

markdown guide