How to test private methods in C#
Tags: csharp, dotnet, backend, unittest
Published: 2023-07-13 06:11:21

## 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`:

```C#
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`:

```C#
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`. 

1. Why not just make `NotifyStudents` public
2. 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`:

1. Create a `Type` object of the class `StudentNotifications`
2. Use the `Activator.CreateInstance` to instantiate the type
3. Get the private method info as `MethodInfo` using `type.GetMethod`
4. Invoke the method passing the `object[]` parameter
5. Verify the result

Let's write the test with all the steps using `xUnit`:

```C#
[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:

```C#
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`:

```C#
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`

```C#
[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`**. 

```C#
[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!!!