Joy
Joy

Reputation: 73

How to unit test an internal method or private method in an abstract class in c#?

I have to write a unit test for an internal or private method in an abstract class in c#. consider this:

Public Abstract Class MyAbstractClass
{
 Private String MyMethod()
 {
   //Do Something
   return "HI";
 }
}

And, my test method:

public void MyTestMethod()
{
    var testProcessor = new Mock<MyAbstractClass>
    {
        CallBase = true
    };
    var privateType = new PrivateType(typeof(MyAbstractClass));
    PrivateObject privateObject = new PrivateObject(testProcessor , privateType);
    var resValue = (String)privateObject.Invoke("MyMethod");
    Assert.AreEqual(resValue ,"HI");
}

but when I run the test method, I'm getting below error:

 System.Reflection.TargetException: Object does not match target type.

How to resolve that?

Upvotes: 5

Views: 2517

Answers (3)

lidqy
lidqy

Reputation: 2463

Assuming you are using Moq as a mocking tool, the exception shown results of passing testProcessor instead of testProcessor.Object. If you change that line to...

PrivateObject privateObject = new PrivateObject(testProcessor.Object, privateType);

...you'll get rid of this error.
This is how it should work (in NET Framework, PrivateObject and such like has not been ported to NET Core MSTest):

//productive code
    public abstract class MyAbstractClass
    {
        private string MyMethod()
        {
            //Do Something
            return "HI";
        }
    }

    //testproject

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var testProcessor         = new Moq.Mock<MyAbstractClass>();
            var privateType           = new PrivateType(typeof(MyAbstractClass));
            var privateObject         = new PrivateObject(testProcessor.Object, privateType);
            var resValue              = (string)privateObject.Invoke("MyMethod");
            Assert.AreEqual(resValue, "HI");
        }
    }

By the way, I' didn't know a mock of an abstract class will execute the implementation of the abstract class. I thought it would create stubs that return defaults, as a mock of an interface does.
Interesting feature...

Upvotes: 2

openshac
openshac

Reputation: 5165

You should not be testing private methods:

I do not unit test private methods. A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation.

See this post for more details.

As Flydog57 says, you should be testing a derived class (not the abstract one).

You can do this easily by creating a private test class inside your test class, and use this private class in your tests.

private class MyConcreteTestClass : MyAbstractClass 
{
     public string SomeMethodThatCallsMethodInAbstractClass()
     {
          ....
     }
}

public void MyTestMethod()
{
    // Arrange
    var testProcessor = new MyConcreteTestClass();

    // Act
    var result = testProcessor.SomeMethodThatCallsMethodInAbstractClass();

    // Asset
    Assert.AreEqual(result ,"HI");
}

Upvotes: 4

Misha Zaslavsky
Misha Zaslavsky

Reputation: 9666

You need to specify the BindingFlags to be NonPublic in order to find the private method.

I am not sure what is the PrivateObject but this should work (you may change it a little bit to make it suitable for your needs):

Type type = typeof(MyAbstractClass);

MethodInfo method = type.GetMethod(
    methodName, BindingFlags.NonPublic | BindingFlags.Instance);

string result = (string)method.Invoke("MyMethod", null);

Upvotes: 0

Related Questions