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
 
No comments:
Post a Comment