Test-driven development (TDD)
TDD is a software development process that relies on the repetition of a very short development cycle: first an (initially failing) automated test case is written that defines a desired improvement or new function. Then, the minimum amount of code is written to pass that test, and finally one refactors the new code to acceptable standards.
Test-driven development cycle
• Add a test
• Run all tests and see if the new test fails
• Write the code
• Run tests
• Refactor code
• Repeat
In case of multi-layered applications, each layer must be tested separately for their functionality, so that the consuming layer gets the correct result. There are various approaches to verify the correctness of each layer in the application. Generally, a manual testing approach is followed for testing an application. But in case of manual testing, a lot of efforts goes in while creating testing scripts and soon the time taken for creating testing scripts becomes a major cost factor. Because of the time and cost involved in manual testing, automated testing is recommended. The advantages of automated testing is that the test code is always available with the application code and can be executed at any time to verify the code changes.
Benefits of Test-Driven Development
• The suite of unit tests provides constant feedback that each component is still working.
• The unit tests act as documentation that cannot go out-of-date, unlike separate documentation, which can and frequently does.
• When the test passes and the production code is refactored to remove duplication, it is clear that the code is finished, and the developer can move on to a new test.
• Test-driven development forces critical analysis and design because the developer cannot create the production code without truly understanding what the desired result should be and how to test it.
• The software tends to be better designed, that is, loosely coupled and easily maintainable, because the developer is free to make design decisions and refactor at any time with confidence that the software is still working. This confidence is gained by running the tests. The need for a design pattern may emerge, and the code can be changed at that time.
• The test suite acts as a regression safety net on bugs: If a bug is found, the developer should create a test to reveal the bug and then modify the production code so that the bug goes away and all other tests still pass. On each successive test run, all previous bug fixes are verified.
- Reduced debugging time.
Mocking framework is used to create replacement objects like Fakes, Stubs and Mocks. It is used to isolate each dependency and help developers in performing unit testing in a concise, quick and reliable way. Mocks are just one of a number of ‘Test Doubles’.
Some of the Latest being used are:
• MOCKS:
• MOQ
• RHINO
• Fakes
MOQ
MOQ (pronounced “Mock-you” or just “Mock”) is the only mocking library for .NET developed from scratch to take full advantage of .NET 3.5 (i.e. Linq expression trees) and C# 3.0 features (i.e. lambda expressions) that make it the most productive, type-safe and refactoring-friendly mocking library available. And it supports mocking interfaces as well as classes. Its API is extremely simple and straightforward, and doesn’t require any prior knowledge or experience with mocking concepts.
IMPLEM ENTATION
Install MOQ using Nuget with following command:
Install-Package Moq
Moq provides us with an easy way of creating mock objects;
Mock<IContainer> mockContainer = new Mock<IContainer>();
Mock<ICustomerView> mockView = new Mock<ICustomerView>();
To access the actual instance of the object, we can access the Object property, which is strongly typed;
Mock<ICustomerView> mockView = new Mock<ICustomerView>();
ICustomerView view = mockView.Object;
To verify that a method was called, use the Verify method on the mock object;
mockCustomerRepository.Verify(t => t.Add(It.IsAny<Customer>()));
Simple example:
[Test]
public void Save_CustomerIsNotNull_GetsAddedToRepository()
{
//Arrange
Mock<IContainer> mockContainer = new Mock<IContainer>();
Mock<ICustomerView> mockView = new Mock<ICustomerView>();
CustomerViewModel viewModel = new CustomerViewModel(mockView.Object, mockContainer.Object);
viewModel.CustomersRepository = new CustomersRepository();
viewModel.Customer = new Mock<Customer>().Object;
//Act
viewModel.Save();
//Assert
Assert.IsTrue(viewModel.CustomersRepository.Count == 1);
}
[Test]
[ExpectedException(ExpectedException = typeof(InvalidOperationException))]
public void When_Adding_Null_Customer_Should_Throw_Invalid_Operation_Exception()
{
ICustomerViewModel viewModel = new CustomerViewModel(_mockView.Object, _mockContainer.Object);
viewModel.CustomersRepository = _mockCustomerRepository.Object;
//Act
viewModel.Save();
//Assert
}
Rhino Mocks
Rhino Mocks is a .Net mocking framework which is extremely useful for stubbing out objects in unit tests to ensure testing what is needed to test and to allow you to control the environment/state/surroundings under which tests are execute.
Rhino Mocks supports three basic types of mock objects:
Strict Mock
A strict mock requires to provide alternate implementations for each method/property that is used on the mock. If any methods/properties are used which has no implementation then an exception will be thrown.
Dynamic Mock
With a dynamic mock, any methods/properties called by tests, for which there is no implementation, will return the default value for the data type of the return value. In other words, it will return 0 for number types, false for Booleans and a null for any object types.
Partial Mock
A partial mock will use the underlying object’s implementation if implementation is not provided.
IMPLEM ENTATION
Creating a Mock
To generate mocks, you’ll use the static factory
Rhino.Mocks.MockRepository. From this factory, you’ll create your mock using one of a few generic methods:
GenerateMock<T> (for DynamicMocks)
GeneratePartialMock<T>
GenerateStrictMock<T>
Sample Rhino Stub example
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RhinoMockProject;
using Rhino.Mocks;
namespace RhinoMockProjectTests
{
[TestClass]
public class RhinoMocksTest
{
private IEnumerable<IProduct> _fakeProducts = new List<IProduct>
{
new Product {Name = “Steak”, Price = 9.85m},
new Product {Name = “Milk”, Price = 2.02m},
new Product {Name = “Diapers”, Price = 33.07m}
};
[TestMethod]
public void TestStubInterfaceMethod()
{
MockRepository mocks = new MockRepository();
IProductRepository products = mocks.Stub<IProductRepository>();
using (mocks.Record())
{
SetupResult.For(products.Select()).Return(_fakeProducts);
}
var results = products.Select();
Assert.AreEqual(3, results.Count());
}
}
}
Microsoft Fakes:
Microsoft Fakes allows you to intercept any method call and provide a substitute implementation, including calls to static and non-virtual methods.
Shim types are one of two technologies that the Microsoft Fakes Framework uses to let you easily isolate components under test from the environment. Shims divert calls to specific methods to code that you write as part of your test. Many methods return different results dependent on external conditions, but a shim is under the control of your test and can return consistent results at every call. This makes your tests much easier to write.
Use shims to isolate your code from assemblies that are not part of your solution. To isolate components of your solution from each other, we recommend that you use stubs.
Add Fakes Assemblies
1. In Solution Explorer, expand your unit test project’s References.
o If you are working in Visual Basic, you must select Show All Files in the Solution Explorer toolbar, in order to see the References list.
2. Select the assembly that contains the classes definitions for which you want to create shims. For example, if you want to shim DateTime, select System.dll
3. On the shortcut menu, choose Add Fakes Assembly.
IMPLEM ENTATION
Sample Example:
namespace FakesTutorial.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class FileReaderTest
{
[TestMethod]
public void TestMethod1()
{
using (Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create())
{
// Arrange
const string path = “irrelevant”;
const string expected = “contents”;
var target = new FileReader(path);
// shim the FileStream constructor
System.IO.Fakes.ShimFileStream.ConstructorStringFileMode =
(@this, p, f) => {
var shim = new System.IO.Fakes.ShimFileStream(@this);
};
// shim the StreamReader constructor
System.IO.Fakes.ShimStreamReader.ConstructorStream =
(@this, s) => {
var shim = new System.IO.Fakes.ShimStreamReader(@this) {
// shim the ReadToEnd method
ReadToEnd = () => expected
};
};
// Act
var actual = target.Read();
// Assert
Assert.AreEqual(expected, actual);
}
}
}
}
Conclusion
MOQ
• It is Open Source.
• Forces to isolate the responsibility to other class so it won’t be clustered in one class.
• Cannot mock static method.
• Need to refactor legacy code and dependency injection (pass the related object to the constructor)
Rhino Mocks
• It is Open Source.
• Force to isolate the responsibility to other class so it won’t be clustered in one class.
• Cannot mock static method.
• Need to refactor legacy code and dependency injection (pass the related object to the constructor)
Fakes & Shim
• Can mock static method
• Better with legacy code
• Compile time is slower because it needs to create Fakes library for the assembly for testing.
• Require MSVS 2012 Ultimate