Introduction
As part of contemporary application development methodologies, unit testing is employed to automate the testing of a single unit of work. Unfortunately some developers carry the belief that only public methods exposed through an interface, can be tested. This blog will describe a method to test private methods.
What is a Unit Test?
A unit test is an automated piece of code that invokes a unit of work in the system and then checks a single assumption about the behavior of that unit of work (Osherove, 2913).
A unit of work is a single logical functional use case in the system that can be invoked by some public interface (in most cases). A unit of work can span a single method, a whole class or multiple classes working together to achieve one single logical purpose that can be verified.
A good unit test is:
- Able to be fully automated
- Has full control over all the pieces running (use mocks or stubs to achieve this isolation when needed)
- Can be run in any order if part of many other tests
- Runs in memory (no DB or File access, for example)
- Consistently returns the same result (you always run the same test, so no random numbers- save those for integration or range tests)
- Runs fast
- Tests a single logical concept in the system
- Readable
- Maintainable
- Trustworthy (when you see its result, you don’t need to debug the code just to be sure)
Unit Testing Example
This section will briefly illustrate a sample private method and how it can be unit tested.
Sample Method
The following is a simple piece of C# code that checks where or not a mail message contains a recipient.
The method is marked private.
private bool ValidateMessage(MailMessage message)
{
if (!message.To.Any())
{
Log.Error("SendMail Email contains NO Recipients");
return false;
}
return true;
}
The above method is part of a mail related service and should not be exposed to other services.
How do we test a private method?
Answer: Use Reflection.
What is Reflection?
By using Reflection in C#, one is able to find out details of an object, method, and create objects and invoke methods at runtime. The System.Reflection namespace contains classes and interfaces that provide a managed view of loaded types, methods, and fields, with the ability to dynamically create and invoke types.
Unit Testing Example
The following is the unit test for the above ValidateMessage method. This test uses xunit, a modern unit testing framework. In the example below a MailMessage is created without a recipient. The unit test is used to validate the ValidateMessage method above.
[Fact]
public void Test_PrivateMethod_ValidateMessage_Returns_False_If_MessageNotValided()
{
// Arrange
var mailService = new MailServices(_sharedFixture.UserEnvironmentServices.Object);
var message = new MailMessage
{
From = new MailAddress($"{Constants.MailSettings.NoReplyAddress}"),
Subject = "Subject",
Body = "Body"
};
// Act
var result = mailService.CallPrivateMethod<bool>("ValidateMessage", message);
// Assert
Assert.NotNull(result);
Assert.IsType<bool>(result);
Assert.False(result);
}
To support unit testing of private methods the following extension methods were created.
Both use Reflection to expose the private method.
Basic
public static object CallPrivateMethod(this object o, string methodName, params object[] args)
{
var mi = o.GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (mi != null)
{
return mi.Invoke(o, args);
}
return null;
}
Generic
public static T CallPrivateMethod<T>(this object o, string methodName, params object[] args) where T : new()
{
var mi = o.GetType().GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (mi != null)
{
return (T) mi.Invoke(o, args);
}
return new T();
}
Conclusion
This blog discussed unit testing and how private methods can be easily tested using Reflection. This allows the programmer to only expose methods that need to be consumed by other services, keeping interfaces light and services simple and easier to use.
Bibliography
Osherove, R. (2913). The Art of Unit Testing, Second Addition. Manning Publications.