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
Top comments (0)