Unit testing private methods in C# can be a challenging task as private methods are not directly accessible from outside the class. However, there are several techniques available to test private methods in C#, including Reflection, Accessors, and Protected Method Testing.
By the end of this article, you will have a better understanding of how to test private methods using XUnit in C# and be equipped with the knowledge to write effective unit tests for your C# projects.
Diffrent Ways of Testing Private Methods in XUnit
Testing private methods is often considered an anti-pattern, as it can lead to tests that are tightly coupled to the implementation details of the code. However, several techniques can be used to test private methods if it’s necessary.
- Reflection: One common way of testing private methods in C# is to use reflection to access the private method and invoke it with the appropriate parameters. This approach can be useful, but it’s important to note that it can also be risky, as changes in the implementation details can break the tests.
- Protected Method Testing: Another technique for testing private methods is to make the method protected and then create a derived class in the test project that exposes the method. This approach is known as the “Protected Method Testing” pattern. However, it can lead to an increase in code complexity and can violate the encapsulation principle.
- Accessors: Another approach is to use accessors, which are generated by Visual Studio when you create a project that uses the .NET Framework. Accessors are special classes that are generated to expose private methods and properties, making it possible to test them. This approach can be useful, but it can also lead to code bloat, as the generated code can be quite verbose.
- Use public methods that call the private methods: If a private method is only called from public methods, we can test the public methods and indirectly test the private methods. This approach is often more effective than testing private methods directly, as it ensures that the tests are focused on the public API of the code.
Here are some code examples for each technique and how they can be used to test private methods in C#.
Consider a class that manages employee details, which includes a private method called GetEmployeeId
that generates a unique employee id for each employee. The GetEmployeeId
method is not exposed publicly and is only used internally within the class to generate ids. We can use various techniques to test this private method and ensure it is working correctly.
public class EmployeeManager { public EmployeeManager() { } public void SaveEmployee(string name, string email, string contactNo) { var empId = GetEmployeeId(); } private string GetEmployeeId() { return Guid.NewGuid().ToString(); } }
Test Private Methods Using Xunit and Reflection
we can use reflection to test the GetEmployeeId
private method. Here’s an example of how we can use reflection to test the private method:
public class EmployeeManagerTests { [Fact] public void GetEmployeeId_Should_GenerateUniqueGuide() { // Arrange var obj = new EmployeeManager(); var methodInfo = typeof(EmployeeManager).GetMethod("GetEmployeeId", BindingFlags.NonPublic | BindingFlags.Instance); // Act var response = methodInfo.Invoke(obj, null); Assert.NotNull(response); } }
Test Result:
Protected Method Testing in XUnit
Assume the GetEmployeeId method was protected in EmployeeManager instead of private. It would have little bit easier for us to test them because protected methods are exposed at least in child classes.
public class EmployeeManager { public EmployeeManager() { } public void SaveEmployee(string name, string email, string contactNo) { var empId = GetEmployeeId(); } protected string GetEmployeeId() { return Guid.NewGuid().ToString(); } }
We need to update the EmployeeManagerTests class inherited from the EmployeeManager class and that’s it we will have the accessibility of the GetEmployeeId method to access and write the test cases around it.
public class EmployeeManagerTests: EmployeeManager { [Fact] public void GetEmployeeId_Should_GenerateUniqueGuide() { // Arrange var response = GetEmployeeId(); Assert.NotNull(response); } }
Test Private Methods using Accessor Classes in Xunit
PrivateAccessor classes are utility classes that provide helper methods to access private or protected methods within a class. I have used one of the NuGet packages to get the benefit of the already available PrivateAccessor class.
dotnet add package Rees.UnitTestUtilities --version 1.1.7
Let’s update the EmployeeManagerTests class to utilize the PrivateAccessor class and test the GetEmployeeId method.
public class EmployeeManagerTests { private readonly EmployeeManager _manager=new(); [Fact] public void GetEmployeeId_Should_GenerateUniqueGuide() { // Arrange var response = PrivateAccessor.InvokeFunction<string>(_manager, "GetEmployeeId", null); Assert.NotNull(response); } }
This is one example of utilizing the PrivateAccessor utility class. However, it has a lot more methods and functions provided to faster the process of writing the unit test cases.
Test Private Methods Using Public Methods in Xunit
To demonstrate this approach I have modified the EmployeeManager class a little bit. Created an entity class that will be used for storing the employee details and the same details will be sent back to the caller.
public class Employee { public string Name { get; set; } public string Email { get; set; } public string Contact { get; set; } public string Id { get; set; } } public class EmployeeManager { public Employee SaveEmployee(string name, string email, string contactNo) { var empId = GetEmployeeId(); var empRecord = new Employee() { Name = name, Email = email, Contact = contactNo, Id = empId }; //Database logic to save Employee return empRecord; } private string GetEmployeeId() { return Guid.NewGuid().ToString(); } }
Now we have to update the EmployeeManagerTest class as well to write the unit test which will indirectly test the GetEmployeeId method.
public class EmployeeManagerTests { private readonly EmployeeManager _manager=new(); [Fact] public void SaveEmployee_Should_Return_EmployeeId() { // Arrange var emp = _manager.SaveEmployee("Deependra", "K", "98765433456785678"); Assert.NotNull(emp.Id); } }
Conclusion
In conclusion, there are three main approaches to testing private methods: Reflection, Accessors, and Protected Method Testing. We have gone through each one of them and tried understanding them with examples.
Ultimately, the choice of approach will depend on the specific situation and context of the project. In some cases, it may be best to avoid testing private methods directly and instead focus on testing through public methods or creating a more modular design.