Translate

Unit Testing

Unit testing is not a new concept. The basic idea is to create a program which will make certain assumptions for a specific piece of code in our application and determine, based on those assumptions, the code produces an expected result or not.

Defining a Unit Test

The program which will perform test is called unit test. An excellent definition of a unit test is:

Unit test is an automated piece of code which invokes method or class being tested and then checks some assumptions about logical behavior of that method or class. Unit test is almost always written using a unit-testing framework. It can be written easily and runs quickly. It is fully automated, trustworthy, readable and maintainable.

A key concept in above definition is that each unit test must test one and only one, behavior of the functionality being tested. For example, a method which returns an object based on the id. The method can behave in either of two ways - If it finds the object with the specified id then it should return the object. If it does not find the object then it should return null. As the method has two behaviors, we would write two unit tests: first to test the case where it finds the object and second to test when it returns null. It can lead for writing many unit tests for a single piece of code. It is okay to have many unit tests, in fact, it is expected. Unit tests are executed automatically and are very fast. So, it does not matter how many test cases we have.

Also, we should not delete unit test just because it succeeds. We should delete a unit test only if we have removed from the application the corresponding functionality being tested. It will ensure that we can run the test each time we add new functionality or make any change in the application by ensuring nothing has been broken by the addition or change.

With Microsoft Test Framework, we can create unit tests as methods which are decorated with the [TestMethod] attribute. The methods must be in a class which is decorated with the [TestClass] attribute.

Structure of Unit Tests

One of the most common patterns which should be followed when writing unit tests is the AAA pattern, in which we write our unit test in three parts:

Arrange: Set up the unit test. For example, we initialize variables and objects so that they are ready to pass in code to be tested.

Act: Invokes the code we want to test. It means invoking a particular method which we are trying to test.

Assert: Analyzes result produced in the Act part. We might verify the return value and any output parameters are what we expected.

Isolation

Another important aspect of unit testing is that it should properly locate the source of problem when it arises. By using unit testing, although we are testing only small pieces of code, we should do the tests in an isolated environment. It is very important to isolate our code from external resources out of our control (databases, web services and file system etc.) as

The resources might fail for unpredictable reasons.

The data yielded by such external resources might be unpredictable and non-repeatable (which really ruins the unit tests).

The resources might not actually be available yet in our development project.

Additional benefit of testing in isolation is that we can test our code even if the external resources are not ready to interact with it yet. Suppose we are working as a team and our piece of code has a dependency on some piece of code from a colleague but that colleague has not completed the piece of code yet. Or suppose our code has a dependency on a completely external component (from a third party or trading partner).

In either case, by isolating our code, we do not need to worry about those dependencies. We can move forward with our development and be ready to go when the rest of team is ready.

Naming Unit Tests

An ignored aspect of creating a good unit test is its name. Nothing prevents us from naming our unit test something as generic as Test1(), it will hardly give us any useful information. A common practice is to name our unit test in a manner which gives developers as much information as possible when the test gets fail. A good pattern of naming unit tests is to concatenate the name of method being tested, the key assumptions and expected result. For example, UploadFile_ValidFileSpecificied_SizeOnServerOverZeroKb().


Properly naming the test classes is also best practice. We should use same name as the class to be tested and suffix Test. For example, HomeControllerTest - which raises an interesting MVC-related point: we can perform unit tests on controller classes.

No comments:

Post a Comment