'.Net core library: How to test private methods using xUnit

The latest xunit framework does not allow test runners in library code when compiled with .Net Core framework (this is allowed for normal Visual Studio code). The solution is to create a separate testing project to run your test cases.

The problem that I am having is that some methods that I want to test are 'private' methods. How can I call those methods from my test project without making their scope 'public'?

The more general question is: How do you test private methods when the public method that uses the private method can not be used? (because of its interactive nature - that is, it has a ReadLine(); contained in it)


Possible solutions:
1) Make the private methods public and rename them in a manner to indicate that they should not be used externally. (Use 'private' or 'internal' as part of the name)
2) Create a ' public static bool Testflag' field that can be set to true in order to bypass the interactive parts of the public method to ensure testing of all its parts.

(Although I have used both of the above methods - I really do not like it because private methods should stay private and flags add a lot of extra complexities. Has someone encountered the same problem? How did you solved it?



Solution 1:[1]

A quick solution is to make private members that you want to test internal.

You can then add an InternalsVisibleTo attribute to your main library's AssemblyInfo.cs. This will allow your test library (and no other library, without reflection) access to the internal methods/classes in your main library.

e.g.

[assembly: InternalsVisibleTo("Library.Tests")]

Solution 2:[2]

For testing internal classes, @Patric's answer is correct.

Private methods are private for a good reason, supposedly. They should not become internal, again for a good reason.

(And some ppl argue that we should not write unit tests for private methods. Why? Well, because they are "private"! But if you are so serious to write unit tests for private methods:)

This is a much better way to test private methods:

public class Hello
{
  private string _firstName { get; set; }
  private string _lastName { get; set; }

  public Hello(string firstName, string lastName)
  {
     _firstName = firstName;
     _lastName = lastName;
  }

  public string HelloMan()
  {
    if (string.IsNullOrEmpty(_firstName))
       throw new Exception("Missing First Name.");
    return this.HelloMan(_firstName, _lastName);
  }

  private string HelloMan(string firstName, string lastName)
  {
     return $"Hello {firstName} {lastName}!";
  }
}

The test goes like:

public class HelloTests
{
  [Fact]
   public void PrivateHelloManShouldBeWellFormated()
   {
      // Arrange
      var firstName = "John";
      var lastName = "Doe";

      Type type = typeof(Hello);
      var hello = Activator.CreateInstance(type, firstName, lastName);
      MethodInfo method = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
      .Where(x => x.Name == "HelloMan" && x.IsPrivate)
      .First();

      //Act
      var helloMan = (string)method.Invoke(hello, new object [] {firstName, lastName});

     //Assert
     helloMan
     .Should()
     .StartWith("Hello")
     .And
     .EndWith("!")
     .And
     .Contain("John")
     .And
     .Contain("Doe");
   }
 }

Reference:

http://anthonygiretti.com/2018/08/26/how-to-unit-test-private-methods-in-net-core-applications-even-if-its-bad/

Solution 3:[3]

Old fashioned stepwise refinement makes it useful to test a private method, to the pragmatist if not the purist.

Taking @Tohid excellent answer one step further, this class can be used to invoke static or instance private methods

public static class TestPrivate
{
    public static T StaticMethod<T>(Type classType, string methodName, object[] callParams)
    {
        var methodList = classType
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Static);

        if (methodList is null || !methodList.Any()) 
            throw new EntryPointNotFoundException();
        
        var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);

        var output = (T)method.Invoke(null, callParams);

        return output;
    }

    public static T InstanceMethod<T>(object instance, string methodName, object[] callParams)
    {
        var classType = instance.GetType();
        var methodList = classType
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);

        if (methodList is null || !methodList.Any()) 
            throw new EntryPointNotFoundException();
        
        var method = methodList.First(x => x.Name == methodName && !x.IsPublic && x.GetParameters().Length == callParams.Length);

        var output = (T)method.Invoke(instance, callParams);

        return output;
    }
}

And then used thus:

        {
            var result = TestPrivate.InstanceMethod<bool>(InstanceOfClass, "privateMethod", new object[] { param1, param2 });

            Assert.True(result);

            var actual = TestPrivate.StaticMethod<myReturnType>(typeof(StaticClass), "privateMethod", new object[] { param1, param2, param3 });

            Assert.True(actual.prop == expected.prop);
        }

Solution 4:[4]

If you need to test private method, then you're code smells. You should review your architecture, while there is no reason to test private properties, or methods. If you need this - it's not a private method, it a public method of another class.

Specially for haters read paragraph 8.2.1 in book Art of Unit Testing 2nd edition. Only public methods (contracts) should be tests and that's it.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Machado
Solution 4