In the previous post, we had setup our basic WCF project to play around with for unit testing. So let’s get to testing! 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.
staticvoidMain(string[] args) { GetNumbersAndAddThem obj = new GetNumbersAndAddThem( new DbGetSomeNumbers(new BaseDbConnection(CONNECTION_STRING)), new NumberFunctions() );
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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] publicinterfaceIService1 {
///<summary> /// Unit tests for service1 ///</summary> [TestClass] [ExcludeFromCodeCoverage] publicclassService1Tests {
///<summary> /// Get data works as expected with a positive number ///</summary> [TestMethod] publicvoidService1_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] publicvoidService1_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] publicvoidService1_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))] publicvoidService1_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] publicvoidService1_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);
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.
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:
///<summary> /// Constructor - new up IService1 with client ///</summary> publicService1() { this._service = new Service1Client(); }
///<summary> /// Constructor - takes in implementation of IService1 ///</summary> ///<param name="service">The IService1 implementation publicService1(IService1 service) { if (service == null) thrownew 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> publicstringGetData(intvalue) { returnthis._service.GetData(value); }
#endregion Public methods
} }
Now that we’re taking in an interface of the service, we can write some unit tests:
#region Public methods ///<summary> /// initialize the mocks ///</summary> [TestInitialize] publicvoidSetup() { this._service = new Mock<iservice1>(); }
///<summary> /// Exception thrown when IService implementation is not provided ///</summary> [TestMethod] [ExpectedException(typeof(ArgumentNullException))] publicvoidService1_NullIService1InConstructor_ThrowsException() { // Arrange / Act Business.WCF.Service1 service = new Business.WCF.Service1(null); }
///<summary> /// Object properly constructed when implementation of IService1 provided ///</summary> [TestMethod] publicvoidService1_ConstructorWithProvidedIService1_NewsCorrectly() { // Arrange / Act Business.WCF.Service1 service = new Business.WCF.Service1(_service.Object);
///<summary> /// Ensure that a string is returned from Service1 when calling GetData ///</summary> [TestMethod] publicvoidService1_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>());
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.