How to avoid or skip some private method for XUnit test?

Viewed 729

Let's say I have written a unit test to test a public method in XUnit.

[Fact]
public void MethodA_WhenSomething_ThenReturnNull()
{
    // Code removed for brevity.

    // Assert
}

This is MethodA.

public void MethodA() {
  MethodOne();
  MethodTwo();
  MethodThree();
}

MethodOne, MethodTwo and MethodTree are all private method. Is there a way to skip a private method (ie MethodTwo) while running my unit test for MethodA? The reason I want to skip methodTwo is because methodTwo calling a stored procedure and it causes error in Xunit. But I know the stored procedure is running fine without issue, so it is okay for me to skip this method.

And the moment, I am using this way.

public void MethodA() {
  MethodOne();

  #if DEBUG == false
    MethodTwo();
  #endif

  MethodThree();
}

If there is a better way, I wish not to put If DEBUG

3 Answers

This kind of problem is typically solved via Moq.Protected.

So, you need to change private accessor to protected at least for MethodTwo.
But I would suggest to change all accessors to protected.

public class SomeClass
{
    public void MethodA()
    {
        MethodOne();
        MethodTwo();
        MethodThree();
    }
    protected void MethodOne() { ... }
    protected void MethodTwo() { ... }
    protected void MethodThree() { ... }
}

With this, the mock setup would look like this:

using Moq.Protected;

...
var mockSomeClass = new Mock<SomeClass>();

mockSomeClass.Protected()
             .Setup("MethodTwo")
             .Verifiable();

Additionally you can setup a Callback to write something out the test output

const string toBeMockedMethodName = "MethodTwo";
mockSomeClass.Protected()
             .Setup(toBeMockedMethodName)
             .Callback(() => TestContext.Progress.Writeline($"{toBeMockedMethodName} has been called."))
             .Verifiable();

References:

If your private method depends on some external service, then you can create make a mock of it and mark it verifiable.

[Fact]
public void MethodA_WhenSomething_ThenReturnNull()
{
    var barService = new Mock<Bar>();
    barService.Setup(x => x.DoSomething()).Verifiable();
    ///
}

public class Foo
{
    public void MethodA()
    {
        MethodOne();
        MethodTwo();
        MethodThree();
    }

    private void MethodThree() => System.Console.WriteLine();

    private void MethodTwo() => new Bar().DoSomething();

    private void MethodOne() => System.Console.WriteLine();
    
}

public class Bar
{
    public void DoSomething() => System.Console.WriteLine("....");
}

There are several ways that you can use to replace the private method. Let's use the MethodRedirect library.

Let's say there is the following class:

public class SomeClass
{
    public void MethodA()
    {
        MethodOne();
        MethodTwo();
        MethodThree();
    }
    private void MethodOne() { }
    private void MethodTwo() =>
        throw new NotImplementedException();
    private void MethodThree() { }
}
var sc = new SomeClass();
sc.MethodA(); // An exception will be thrown here.

The above library does not have a nuget package. Therefore, we simply copy three files from sources: Extensions.cs, MethodOperation.cs, MethodToken.cs.

Then write the following unit test:

[Fact]
public void MethodA_WhenCalled_NotThrow()
{
    var sut = new SomeClass();
    var fake = new Fake();

    MethodInfo privateMethod = sut.GetType().GetMethod("MethodTwo", BindingFlags.Instance | BindingFlags.NonPublic);
    MethodInfo redirectMethod = fake.GetType().GetMethod("Redirect", BindingFlags.Static | BindingFlags.NonPublic);

    var token = privateMethod.RedirectTo(redirectMethod);

    sut.MethodA(); // should not throw

    token.Restore();
}

Auxiliary class whose method will be used as a replacement

public class Fake
{
    static void Redirect() { }
}
Related