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
35
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
26
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
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]
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
128
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
26
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
54
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
71
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

Author

Russ Hammett

Posted on

2016-01-24

Updated on

2022-10-13

Licensed under

Comments