35

I want to write unit test for private method in C# using moq framework, I've search in StackOverFlow and Google, but I cannot find the expected result. Please help me if you can.

LeftyX
  • 35,328
  • 21
  • 132
  • 193
PeaceInMind
  • 1,147
  • 3
  • 11
  • 32

5 Answers5

67

You can't, at least not with Moq.

But more importantly, you shouldn't. First off, you don't test methods, you test behaviours. Second, in order to test behaviours, you exercise a type's public API and verify the outcomes of that exercise.

Private methods are implementation details. You don't want to verify how things get done, you want to verify that things do get done.

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • 2
    Wouldn't it be more apropriate to implement the Single Responsiblity Principle and refactor the private methods in question into their own class? Then your testing the public behavior of each class. – w00ngy Dec 01 '16 at 15:16
  • @w00ngy - That's a big, and common, misrepresentation of what the SRP is. See this post by its author, Rob C Martin: https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html. Private methods do not break the SRP, and they're indeed very import to OOP design. There's a reason why methods in C# and Java are assumed to be private when the access modifier is omitted. – dcastro Dec 02 '16 at 11:51
  • 3
    Good advice when dealing with greenfield. Brownfield development does not have that luxery. How do you test a class with 3,000 lines of code, 30 concrete dependencies, including statics and no DI? I need a tool with more flexibility than this. Microsoft Fakes appears to have most if not all of this functionality. But it's only available in Visual Studio 2017 Enterprise. I need a free tool. – P.Brian.Mackey Apr 06 '18 at 19:56
  • 4
    I'm on the other side of the fence. Private functions should be tested for correctness as a unit test. One benefit is that errors point you to the actual erroring code, not some block that's using your private function that who knows how long. I just wish more libraries supported this. – nullsteph Oct 09 '18 at 01:10
  • @nullsteph If your class is big enough that you'd consider a public method "some block that's using your private function", and not a cohesive unit, then I think you've got a case of the God Class. Consider extracting things out. – dcastro Oct 09 '18 at 06:02
  • 1
    But it's not uncommon for the private method to involve some process that's supposed to be mocked, e.g. a network operation. That's the part where mocking is supposed to do its job and benefit the developers. – Nico Dec 11 '18 at 08:01
  • @Nico that sounds like a design issue. 1) side effects should be controlled, not spread out and 2) inversion of control. I don't think testing frameworks should cater to problematic design - they should encourage good design. – dcastro Dec 11 '18 at 08:14
  • 2
    @dcastro so how do you propose to fix the design that involves a network operation inside a private method? – Nico Dec 11 '18 at 12:25
  • @Nico inversion of control – dcastro Dec 11 '18 at 12:38
  • @dcastro and if the code isn't ready to be injected? – Arjan Oct 09 '20 at 11:44
17

In the AssemblyInfo.cs of your project add

[assembly: InternalsVisibleTo("Namespace.OfYourUnitTest.Project")]

then you make the method internal instead of private.

It has the benefit of avoiding to make it public.

However as pointed by dcastro, some people strongly disagree with this way of testing.

alexandrekow
  • 1,927
  • 2
  • 22
  • 40
14

Perhaps you shouldn't (see other answers for why), but you can do this using Microsoft's Visual Studio Test Tools. A simplified example is given below.

Given the following class which you want to test:

public class ClassToTest
{
    private int Duplicate(int n)
    {
        return n*2;
    }
}

You can use the following code to test the private Duplicate method:

using Microsoft.VisualStudio.TestTools.UnitTesting;

// ...

[TestMethod]
public void MyTestMethod()
{
    // Arrange
    var testClass = new ClassToTest();
    var privateObject = new PrivateObject(testClass);

    // Act
    var output = (int) privateObject.Invoke("Duplicate", 21);

    // Assert
    Assert.AreEqual(42, output);
}
Community
  • 1
  • 1
Grinn
  • 5,370
  • 38
  • 51
7

Simply, you don't. Private methods are not visible to other classes.

There are a number of ways around this:

  • Treat the private as part of the method you're testing, cover it in their unit tests. Think of the public methods as black boxes and test their operations.
  • Make it protected and inherit your test class from the class you're testing (or use a partial - same idea)
  • Make it public (which if you're coding to an interface doesn't actually expose it to your consumers)

For public methods (option three) it is possible to partial mock the class where you can replace the method. In Moq you can do this like this:

var moq = new Mock<MyClass>();
moq.CallBase = true;
moq.Setup(x => x.MyPublicMethodToOverride()).Returns(true);

There are more details here.

Community
  • 1
  • 1
Liath
  • 9,913
  • 9
  • 51
  • 81
7

Moq supports mocking protected methods. Changing the methods to protected, instead of private, would allow you to mock their implementation.

The following is from Moq Quickstart Documentation (deep link):

Setting expectations for protected members (you can't get IntelliSense for these, so you access them using the member name as a string). Assuming the following class with a protected function should be mocked:

public class CommandBase {
    protected virtual int Execute();            // (1) 
    protected virtual bool Execute(string arg); // (2)
}
// at the top of the test fixture
using Moq.Protected;

// In the test, mocking the `int Execute()` method (1) 
var mock = new Mock<CommandBase>();
mock.Protected()
     .Setup<int>("Execute")
     .Returns(5);

// If you need argument matching, you MUST use ItExpr rather than It
// planning on improving this for vNext (see below for an alternative in Moq 4.8)
// Mocking the `bool Execute(string arg)` method (2)
mock.Protected()
    .Setup<bool>("Execute",
        ItExpr.IsAny<string>())
    .Returns(true);

Moq 4.8 and later allows you to set up protected members through a completely unrelated type that has the same members and thus provides the type information necessary for IntelliSense to work. Pickin up the example from the bullet point above, you can also use this interface to set up protected generic methods and those having by-ref parameters:

// Completely unrelated Interface (CommandBase is not derived from it) only created for the test.
// It contains a method with an identical method signature to the protected method in the actual class which should be mocked
interface CommandBaseProtectedMembers
{
    bool Execute(string arg);
}

mock.Protected().As<CommandBaseProtectedMembers>()
    .Setup(m => m.Execute(It.IsAny<string>()))  // will set up CommandBase.Execute
    .Returns(true);
Jesse Johnson
  • 1,638
  • 15
  • 25
  • And for the sake of best practice, access modifiers should not be changed for the sole purpose of unit testing. – fix Dec 01 '22 at 19:18