Test a public method which calls a private method using NUnit

元气小坏坏 提交于 2019-12-22 08:39:03

问题


I have a public method in a class that internally calls a particular private method within that class. It looks something like this :

public class MyClass : IMyClassInterface
{
    public List<int> MyMethod(int a, int b)
    {
        MyPrivateMethod(a, b, ref varList, ref someVal);
    }
    private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
    {
    }
}

Now, I basically want to test this public method using NUnit. I am using NMock 2.0 for mocking. How do I do it? Since, it internally calls this private method which I do not want to make public. Or is there a way to do it if I turn the private method to protected instead?


回答1:


Now, I basically want to test this public method (...)

This is great. This is what you should be doing. Forget about internal details for a moment. From public method point of view, is there any difference between these two snippets?

// Your current implementation
public void MyMethod(int a, int b)
{
    MyPrivateMethod(a, b);
}
private void MyPrivateMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

// Private method inlined
public void MyMethod(int a, int b)
{
    var c = a + b;
    // some more code
}

Whoever calls (public) MyMethod will not be able to notice any difference between these two. End result is the same. It doesn't matter there is a private method call, because as far a public API is concerned it is irrelevant. You could inline said private method, make it gone forever, and from public consumer point of view nothing changes. End result is the only thing that's important. You test end result observable by code consumer. Not some internal gibberish.

Important realization is this:

Properly designed SOLID code will never put you in a position which will require you to do private mocking. Source of the problem? Bad design.

Source: How to mock private method - solutions

Yep. Sad but true, your design is not that great. Depending on whether you want to change that or not, there are few approaches you could take:

  • don't try to mock private details, focus on public API (doesn't help with design issue)
  • extract private method to class, introduce dependency (long-term solution, improves design and makes code easily testable)
  • make private method protected, override in test as suggested in other answer (doesn't help with design issue, might not yield valuable test)

Whichever you chose I leave up to you. However, I'll emphasize it one more time - mocking private method is not unit testing, library or tools problem - it is a design problem and is best solvable as such.


On a side note, (if you can) don't use NMock2. It's a library with last changes from 2009. It's like having a 30 year old car which was last serviced 15 years ago. There are much better ones nowadays (FakeItEasy, Moq, NSubstitute).




回答2:


Yes the "trick" is to use protected instead of private and then inherit the class and run the test on the new class that executes the protected method. This is a very common way to make brownfield and legacy code testable.

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            MyClassTestWrapped t = new MyClassTestWrapped();
            Assert.IsTrue(t.MyPrivateMethod(...));
            Assert.IsTrue(t.MyMethod(...));

            MockFactory _factory = new MockFactory();
            Mock<MyClassTestWrapped> mock;

            mock = _factory.CreateMock<MyClass>();
            mock.Expects.One.MethodWith(d => d.MyPrivateMethod());  // do the nmock magic here


        }
    }

    public class MyClass : IMyClassInterface
    {
        public List<int> MyMethod(int a, int b)
        {
            MyPrivateMethod(a, b, ref varList, ref someVal);
        }
// here change to protected
        protected void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
        {
        }
    }

    public interface IMyClassInterface
    {

    }

    public class MyClassTestWrapped : MyClass
    {
        public List<int> MyMethod(int a, int b)
        {
            base.MyMethod(a, b);
        }

        public List<int> MyPrivateMethod(int a, int b,ref List<int> varList, ref double someval)
        {
            base.MyPrivateMethod(a, b, ref varList, ref someval);
        }

    }



回答3:


While currently you have to refactor your code to lose the private modifier ( wrappers and what not), You can do it fairly easy with tools Like Typemock Isolator.

I added some code to your example to write the test:

public class MyClass 
{
    public List<int> MyMethod(int a, int b)
    {
        List<int> varList = new List<int>();
        double someVal = 0;

        MyPrivateMethod(a, b, ref varList, ref someVal);

        return varList;
    }

    private void MyPrivateMethod(int a, int b, ref List<int> varList, ref double someval)
    {
    }
}

With this straight forward approach you just fake the private method as it is in your code (no changes in the production), even it's ref parameters:

[Test]
public void TestMethod1()
{
    //Arrange
    var myClass = new MyClass();
    var expectedVarList = new List<int> {1,2,3};

    Isolate.NonPublic.WhenCalled(myClass, "MyPrivateMethod")
        .AssignRefOut(expectedVarList, 0.0)
        .IgnoreCall();

    //Act
    var resultVarList = myClass.MyMethod(0, 0);

    //Assert
    CollectionAssert.AreEqual(expectedVarList, resultVarList);

}


来源:https://stackoverflow.com/questions/31308322/test-a-public-method-which-calls-a-private-method-using-nunit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!