Introduction
A quick search online tells us that we are doing something wrong by testing private methods.
As a matter of fact we shouldn't be testing the private methods. Tests are for public interfaces.
The reasons and when we might need this is another discussion, let's concentrate on what we can do if we must.
Enough talk! Let's jump right into the code.
Implementation
Let's define a simple POCO class Student
:
public class Student
{
public string Name { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
public bool Notified { get; set; }
}
Next, let's add a dummy class TestPrivateMethod
that has a public method SendNotifications
calling private methods NotifyStudents
and SaveNotifyStatusToDb
:
namespace TestPrivateMethod
{
public class StudentNotifications
{
public bool SendNotifications(Student student)
{
var success = NotifyStudents(student);
if (success)
{
var code = SaveNotifyStatusToDb(student);
if (code <= 0) { return false; }
}
return success;
}
private bool NotifyStudents(Student student)
{
if (!student.IsActive || student.Notified)
{
return false;
}
// some code to simulate event call and get the status
return true;
}
private int SaveNotifyStatusToDb(Student student)
{
// some code to simulate save to db
return 1;
}
}
}
Since SendNotifications
relies on both NotifyStudents
and SaveNotifyStatusToDb
: we can't test either private method.
Although we could refactor, we decided not to do that.
We still want to be able to test NotifyStudents
independently.
Let's see what general options we have to test NotifyStudents
.
- Why not just make
NotifyStudents
public - Move
NotifyStudents
to a seperate class of its own
It makes no sense to make a private method public, so neither solution seems sensible.
Testing Private Methods using Reflection
We can invoke a private method utilizing MethodInfo
:
- Create a
Type
object of the classStudentNotifications
- Use the
Activator.CreateInstance
to instantiate the type - Get the private method info as
MethodInfo
usingtype.GetMethod
- Invoke the method passing the
object[]
parameter - Verify the result
Let's write the test with all the steps using xUnit
:
[Fact]
public void When_Active_Then_True()
{
// get the type
Type type = typeof(StudentNotifications);
// create the object of the type
var studentNotifications = Activator.CreateInstance(type);
// get the private method
MethodInfo method = type.GetMethod("NotifyStudents",
BindingFlags.NonPublic | BindingFlags.Instance);
// prepare parameters object accepted by the private method
object[] parameters = {
new Student
{
Name ="TestA",
Notified=false,
IsActive=true
}
};
// invoke the method
bool result = (bool) method.Invoke(
studentNotifications,
parameters);
// verify the result
Assert.True(result);
}
The test runs!! The issue is though that the test might break:
type.GetMethod(
"NotifyStudents", // this is not strongly typed
BindingFlags.NonPublic |
BindingFlags.Instance)
What happens when someone renames the NotifyStudents
? The change will not auto-updated in the test.
We would require to modify the test to make it work again.
Certainly, we do not adhere to the principle that unit tests should work always once written.
Next, let's have a look on another way of testing the private methods.
Testing Private Methods using Wrapper Function
Let's add a wrapper function NotifyStudentWrapperFunction
in class StudentNotifications
:
internal protected bool NotifyStudentWrapperFunction(Student student)
{
return NotifyStudents(student);
}
NotifyStudentWrapperFunction
simply calls the NotifyStudents
passing the required parameter.
Doesn't it defeat the purpose of NotifyStudents
being private in the first place?
The detail is in the NotifyStudentWrapperFunction
accessibility - it is marked with internal protected
.
The accessibility restriction makes this method inaccessible to outside world.
Now, let's check if we can test NotifyStudents
using the NotifyStudentWrapperFunction
[Fact]
public void When_Active_Then_True_Using_Wrapper_Function()
{
var sut = new StudentNotifications();
var result = sut.NotifyStudentWrapperFunction(
new Student
{
Name = "TestA",
Notified = false,
IsActive = true
});
// verify the result
Assert.True(result);
}
We have compile error for sut.NotifyStudentWrapperFunction
. That's a no brainer; we can't access internal protected
members outside their own assembly.
Then how are we supposed to use the wrapper function?
There comes the .Net framework to our rescue - the framework provides us with a very useful attribute to help us with unit testing - InternalsVisibleTo
.
[assembly: InternalsVisibleTo("TestPrivateMethod.Tests")]
namespace TestPrivateMethod
{
public class StudentNotifications {....}
internal protected bool NotifyStudentWrapperFunction(
Student student) {....}
}
We can mark the namespace with InternalsVisibleTo
to make the internal protected
method visible to the specified assembly - in this case the unit test assembly.
Now, we have no error at sut.NotifyStudentWrapperFunction(...)
. Test runs successfully.
Conclusion
We learned how we can test private methods - the pitfalls of using reflection for the purpose; a better alternative to it - using a wrapper function respecting the accessibility level of the method.
Leave a comment, share and tell us what other ways we could test the private methods.
Happy coding!!!
Top comments (2)
Nice article. But doesn’t this just bloat your StudentNtotifications class with a lot of wrapper classes for testing ?
I guess if there were many others you’d refactor into a different class / service / pattern ?
Great article!!
Thanks :)