I'm often asked whether you should write unit tests for private or protected methods. Or how to test private methods in C#. Today I will try to briefly answer these questions. To make it easier for you to understand this problem, I will present it with an easy example.
How to Test Private Methods?
There are several popular ways to test private methods. You can simply change the access modifier of such a method, then this method will immediately be visible in the test. However, this is usually not a good solution, because if a given method is to be private, its accessibility level should not be changed just for the sake of testing (in some cases it makes sense, but we will discuss this later in this article). This just adds clutter to our code. Other ways are to use the reflection mechanism or the InternalsVisibleTo attribute. However, I will not elaborate on how exactly to implement such tests, because in both cases it will not be a good solution either. If you need to test a private method, it is most likely a sign that you have a bad architecture in your application. So, you should probably start with refactoring.
Should You Test Private and Protected Methods?
Let's look at a simple example.
public class Offer
{
public string Title { get; private set; }
public event EventHandler OfferChanged;
public void SetTitle(string title)
{
if (string.IsNullOrWhiteSpace(title))
throw new ArgumentNullException(nameof(title));
Title = title;
OnOfferChanged(Guid.NewGuid());
}
protected virtual void OnOfferChanged(Guid id)
{
OfferChanged?.Invoke(this, id);
}
}
Here we have a SetTitle method in the Offer class that simply sets the Title property if the parameter is not null, and an empty string, and fires the OfferChanged event. Let's write a short test to make sure that the OfferChanged event is fired when a valid parameter is passed to the method. The test might look like this:
[Test]
public void SetTitle_WhenCalled_ShouldRaiseOfferChangedEvent()
{
var offer = new Offer();
using (var monitoredSubject = offer.Monitor())
{
offer.SetTitle("1");
monitoredSubject.Should().Raise("OfferChanged");
}
}
If the value 1 is passed to the method, then the OfferChanged event is triggered. Now let's consider whether additional tests should be written for the non-public OnOfferChanged method. As you can see, this is a detail of our implementation that has already been tested in the SetTile public method tests. It is also not necessary to write additional tests for the OnOfferChanged method. However, if you decide to write tests for this method, you must also remember that the method, which is an implementation detail, can be changed much more often. At this point, the id parameter is passed to the OnOfferChanged method, but it could just as easily be taken from the field of the Offer class.
public class Offer
{
private Guid _id;
public string Title { get; private set; }
public event EventHandler OfferChanged;
public Offer()
{
_id = Guid.NewGuid();
}
public void SetTitle(string title)
{
if (string.IsNullOrWhiteSpace(title))
throw new ArgumentNullException(nameof(title));
Title = title;
OnOfferChanged();
}
protected virtual void OnOfferChanged()
{
OfferChanged?.Invoke(this, _id);
}
}
In this case, you would have to change your tests because a parameter was removed from the method under test. This is just a simple example, but if the example were more complicated, it would be even more likely to change to a private method. Then these tests would be fragile and would often require updates and changes, which is certainly not the desired effect.
Unit Testing School
If you would like to learn how to write unit tests in C#/.NET applications, consider joining the Unit Testing School - [here].
SUMMARY:
If a method is private, it is called in the public method (at least it should be like that :) ), if we choose the test cases well, when testing the public method, we will also test the private method, because it only contains implementation details. We are interested in whether a given public component works as intended, not the details of its implementation. However, if it turns out that it would be worth testing other test cases, then maybe it should not be a private method, maybe it should be a public method, even separated into another class. Treated as a separate unit and then you definitely need to write separate tests for it. We should only test public APIs, and private and protected methods are just an implementation detail for which we should not write separate unit tests.