In our previous post, Unit Testing in C#, Calculator class under test does not have any dependencies. We need not create any other class instance other than Calculator’s. But this is not usually the case in production environment. Here we will see unit testing approach for the classes having one or more dependencies.
Before we start to explain mocking, remember that one of the concepts of Unit testing is isolation of class under test; this means that dependencies of the class under test should have minimum or no affect on test outcome. This becomes possible when we can mimic the dependencies in the test class with objects which we can control and dictate.
‘Mock’ is something which is not real, is an imitation of a real object. So, Mocking in Unit tests is creating replica objects of real dependencies of class under test, which we can control in test environment.
Let’s start by how we create these mock objects. We can implement mock objects by ourselves or better we should take help of mocking frameworks available such as NMock, Moq for C#, Gmock for C++, Mockito, JMock, EasyMock for Java.
Here for the demonstration, I have a Logger class which has two dependencies, IWriter and IDateTimeProvider, responsibilities for each is evident from the names of interfaces. Logger methods for e.g., LogInfo calls Write method of IWriter‘s implementation. Thus we need to verify in our unit tests whether call to the Write method is done with desired argument or not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now, in unit test class for creating Logger instance, we need not to create instance of its dependencies, instead we can create mock objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In above LoggerTest class, we have used Moq framework, which gives powerful APIs such as Setup and Verify; using Setup, we can arrange desired return value from the mocked object’s properties and methods; using Verify, we can assert whether the call to the mocked object’s method is created or not.
For creating mock objects, Moq gives Mock<T> type where T is the type for which mock object is to be created. Usually for creating mock objects, dependencies of the class are preferred to be of interface types which both results in better abstraction and loose coupling.
Moq library can be download as nuget package from nuget website. Extract the package with WinZip or 7Zip and refer Moq.dll as assembly reference in your unit test project.
Testing any product system becomes necessary to verify its usability and validate its intended purpose. If this testing procedure can be executed on every modification of the product system as a sanity check, it becomes a measure to alleviate chances of any discrepancy introduced.
Thus, Unit testing in software development plays a important role to verify intend and pacify chances of bug instigation on the very basic unit of software. Unit testing is a level of software testing where individual units of software are tested against logical flaws. In object oriented programming, the smallest unit is usually a ‘method‘.
How to start writing Unit Tests
Identify logical unit, usually a method or a property which needs to be tested.
Create setup in the unit test class to invoke that method.
Initiate the call/trigger to the method in focus from your test method.
And the last step, compare the actual results/outcome of the call to the expected result/outcome.
We can start with a simple implementation of a calculator and its unit tests.
Lets take basic methods of a calculator, Add and Subtract. I will show you how we can create multiple tests cases on these simple methods.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Now, we will see the unit tests corresponding to these two methods.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As you can see, these test cases are logical scenario based, it is not enough to test the method with only positive scenario as our implementation usually breaks for negative and borderline scenario, so it becomes necessary for a effective unit test to verify these corner cases.
Also, as a better design practice, try not to club two or more ‘Assert‘ in one test method, one assertion in one method makes easier to identify and pin-point the failure scenario when tests fail.
Further, to increase readability few things should be kept in mind, first, unit test method name should preferably include name of method under test, scenario and expected result; second, follow AAA pattern i.e. divide your test method in Arrange-Act-Assert sections.
Arrange – creating instances, Act – invoking the method under focus and Assert – comparing expected and actual results.
Here unit tests are written using one of the most popular and intuitive unit testing framework in .NET – MsTest. NUnit is also a good option but MsTest is preferred because of its seamless integration with Visual Studio IDE.