Saturday, March 26, 2011

Unit testing – stay focused on your code

Unit testing is a method by which individual units of source code are tested to determine if they are fit for use. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual function or procedure. In object-oriented programming a unit is usually a method. Unit tests are created by programmers or occasionally by white box testers during the development process [ref: Wikipedia]

Generally there could be multiple test cases for testing one functional code flow. There could be standard set of test methods which applies to all the functions. They can be enumerated as follows:

1) TODO: Check if the function gets called in the requested code flow with the given parameters and exceptions are raised in case if any.

2) TODO: Check if the function just executes without any exception or errors. The return value doesn’t really matter for this unit test case to succeed.

3) TODO: Check if the dependency functions are called with the parameters of the expected type (or subtype).

4) TODO: Check if the function returns the type that it is expected to return.

5) TODO: Check if the function returns the exact data that it is expected to return.

The unit testing of the functionality is said to be complete only if all the unit test case for the functionality passes.

The unit testing for all the components can be done together or in the isolation. The effectiveness of the unit testing increases when the code is tested in isolation. It ensures the developer gets the enough code coverage while writing the tests and at the same time it isolates the exception of the dependent code from the CUT (code under test). The following code snippet gives a clear understanding on how the unit testing of the component is done in the isolation. For more details please refer to stubs and mocks.

[TestMethod]

[ExpectedException(typeof(ArgumentException))]

public void CheckIfGetCustomerByIdFailsForCustomerId()

{

var customerProvider = new CustomerProvider(null);

Customer customer = customerProvider.GetCustomerByID(string.Empty);

}

[TestMethod]

public void CheckIfGetCustomerByIdExecutesFine()

{

MockRepository mockRepository = new MockRepository();

ProductProvider productProviderMock = (ProductProvider)mockRepository.CreateMock(typeof(ProductProvider));

var customerProvider = new CustomerProvider(productProviderMock);

Customer customer = customerProvider.GetCustomerByID("ababca");

Assert.IsNotNull(customer);

}

[TestMethod]

public void CheckIfGetCustomerByIdExecutesFineWithDependents()

{

var productProviderMock = MockRepository.GenerateMock();

productProviderMock.Stub(a => a.GetProducts("abc")).IgnoreArguments().Return(new List());

var customerProvider = new CustomerProvider(productProviderMock);

customerProvider.GetCustomerByID("abc");

productProviderMock.VerifyAllExpectations();

}

[TestMethod]

public void CheckIfGetCustomerByIdExecutesFineWithDependentsCheckParameters()

{

var productProviderMock = MockRepository.GenerateMock();

productProviderMock.Expect(a => a.GetProducts("abc")).IgnoreArguments().Return(new List());

var customerProvider = new CustomerProvider(productProviderMock);

customerProvider.GetCustomerByID("abc");

IList args = productProviderMock.GetArgumentsForCallsMadeOn(a => a.GetProducts("abc"));

productProviderMock.VerifyAllExpectations();

}

[TestMethod]

public void CheckIfGetCustomerByIdExecutesFineWithDependentsVerifyResults()

{

var productProviderMock = MockRepository.GenerateMock();

List products = new List();

products.Add(new Product(){ID=1, Name="Product1"});

products.Add(new Product(){ID=2, Name="Product2"});

productProviderMock.Stub(a => a.GetProducts("abc")).IgnoreArguments().Return(products);

var customerProvider = new CustomerProvider(productProviderMock);

var customers = customerProvider.GetCustomerByID("abc");

Assert.IsNotNull(customers, "The customers are null");

Assert.AreEqual(customers.CompanyName, "company");

}

The method of automated unit testing is helpful in the real sense when the code base has much of the functionalities implemented. It may become a nightmare for a developer to manually test all the code at once before it is shipped to the production. Hence it is advisable for all the developers to create the set of their own unit tests for the functionalities they have implemented. This helps the team to ensure that the code written by an individual developer does not break the other functionalities. It is always a good idea to develop the habit of running all the unit test cases before every checkin so as to ensure that the code is stable and can be deployed to production at any point of time.

Stay focused, stay free!

No comments: