Getting Started with Unit Testing and Moq Part 4

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.

Part 1
Part 2
Part 3
Part 4 you are here

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×