Mocking frameworks provide an added ability over reflection
and the code you have just seen. There are freely-available mocking frameworks
available online. Some mocking frameworks are interface-based, meaning that
you need to use an interface to define the object. The framework this article
is going to demo is TypeMock. TypeMock has the ability to mock an entire
class, its properties, and methods. This means that you could mock a data
layer, a business class, or any other object you desire, and it has the ability
to do this by dynamically intercepting the call to it and instead returning the
mock.
TypeMock also features the ability to inspect the operation
of a method, ensuring that everything works as normally expected. Because you
can change the internal value, you have the ability to mock the returned value
from a method call inside a method, and therefore perform more strenuous
testing on an object. This allows you to test your methods more in-depth and
get more specific with your unit tests. Rather than explain all the details,
let us look at an example. To work with mocks, the MockManager must be used.
MockManager must call Init() before running tests and must call Verify() to
verify the tests. These calls usually occur in the setup and teardown methods
in an NUnit test.
Listing 3
[TestFixtureSetUp]
public void Initialize()
{
MockManager.Init();
}
[TestFixtureTearDown]
public void Shutdown()
{
MockManager.Verify();
}
So let us work with objects and setup some expectations.
The code sample attached uses a business and data layer. To obtain a mock, all
we need to do is the following.
Listing 4
Mock _gatewayMock = MockManager.MockAll(typeof(UsersDataGateway));
Because we have a mock, we will still need to instantiate
the object somehow; however, because UsersDataGateway DAL class is used in the
business layer, that instantiation will suffice. All instances of the DAL
layer are mocked. There is also a MockManager.Mock method that mocks a class
one time. Now that we have our mock, we can setup expectations, mock returned
values, test unexpected exceptions, or let the original method be called.
Below are several expectations defined in later unit tests. The
expectation expects a certain method to be called and mocks the desired result.
There are other methods that you can use like ExpectCall, just to verify that a
method exists.
Listing 5
_gatewayMock.ExpectAndThrow("GetDatabase", new InvalidCastException(), null);
_gatewayMock.ExpectAndReturn("GetDatabase", null, null);
In addition, you can use the ExpectGet and ExpectSet methods
to perform property expectations as well. For a full reference to the possible
expectations, please see the TypeMock
documentation.
Take a look at the following unit tests.
Listing 6
[Test]
public void TestMockingDAL()
{
UserProvider provider = new UserProvider();
_gatewayMock.ExpectAndThrow("GetDatabase", new InvalidCastException(), null);
try
{
provider.AddUser("Test", "test@gmail.com");
Assert.Fail("Invalid cast exception should be thrown");
}
catch (InvalidCastException ex){}
_gatewayMock.ExpectAndReturn("GetDatabase", null, null);
try
{
provider.AddUser("Test", "test@gmail.com");
Assert.Fail("Null exception should be thrown");
}
catch (NullReferenceException ex){}
}
The tests above are ensuring that with the expectations and
mocked value, the right result occurs. In addition, you can mock the results
that are returned from a method. The following is a test for mocking the
GetUsers() method, which mocks the DataTable returned. The method passes in a
predefined one that is more useful for unit testing the creation of the Users
array. So, when calling GetUsers for the data provider, the real method does
not run and the fake object is returned.
Listing 7
[Test]
public void TestUsingDAL()
{
UserProvider provider = new UserProvider();
DataTable resultsTable = new DataTable();
resultsTable.Columns.Add("Name", typeof(string));
resultsTable.Columns.Add("Email", typeof(string));
DataRow resultsRow = resultsTable.NewRow();
resultsRow["Name"] = "Brian";
resultsRow["Email"] = "brian@gmail.com";
resultsTable.Rows.Add(resultsRow);
resultsRow = resultsTable.NewRow();
resultsRow["Name"] = "Ted";
resultsRow["Email"] = "ted@gmail.com";
resultsTable.Rows.Add(resultsRow);
resultsRow = resultsTable.NewRow();
resultsRow["Name"] = "Jerry";
resultsRow["Email"] = "jerry@gmail.com";
resultsTable.Rows.Add(resultsRow);
_gatewayMock.ExpectAndReturn("GetUsers", resultsTable, null);
User[]users = provider.GetUsers();
Assert.IsNotNull(users);
Assert.AreEqual(3, users.Length);
Assert.AreEqual("Brian", users[0].Name);
Assert.AreEqual("Ted", users[1].Name);
Assert.AreEqual("Jerry", users[2].Name);
}
In these examples I do not actually do anything against the
database; all of this is done through mocking, which I either do not actually
run the command code or mock the returned DataTable when retrieving users.
That is the power of mocking and this framework, in that you can control
testing to that level.