Testing the internals of a method with RhinoMock o

2019-07-18 18:48发布

问题:

Quite new to this mocking thing, I have a couple of questions.

Correct me if I'm wrong: Mocking does not initialize the real method i.e. Mocking will not actually call the constructor of your class. Instead it does something like look at the signature of the class and create an object with that signature but with none of the methods functionality. This is useful if you just need an object of that type but don't want to test it's internals, e.g. if the object you're actually testing has a dependency on it.

I'm trying to test the internals of a method, does this mean I must create an instance of the class that the method belongs to?

回答1:

Summary: Dynamic mocks will not help you testing the internals of your types, but you shouldn't be attempting to do that in the first place.


You are basically correct in your description, but it's a bit more complicated than that. Essentially, dynamic mocks don't do anything you couldn't do by hand.

Let's say you are programming against an interface such as this one:

public interface IMyInterface
{
    string Foo(string s);
}

You could manually create a test-specific implementation of IMyInterface that ignores the input parameter and always returns the same output:

public class MyClass : IMyInterface
{
    public string Foo(string s)
    {
        return "Bar";
    }
}

However, that becomes repetitive really fast if you want to test how the consumer responds to different return values, so instead of coding up your Test Doubles by hand, you can have a framework dynamically create them for you.

Imagine that dynamic mock really write code similar to the MyClass implementation above (they don't actually write the code, they dynamically emit the types, but it's an accurate enough analogy).

Here's how you could define the same behavior as MyClass with Moq:

var mock = new Mock<IMyInterface>();
mock.Setup(x => x.Foo(It.IsAny<string>())).Returns("Bar");

In both cases, the construcor of the created class will be called when the object is created. As an interface has no constructor, this will normally be the default constructor (of MyClass and the dynamically emitted class, respectively).

You can do the same with concrete types such as this one:

public class MyBase
{
    public virtual string Ploeh()
    {
        return "Fnaah";
    }
}

By hand, you would be able to derive from MyBase and override the Ploeh method because it's virtual:

public class TestSpecificChild : MyBase
{
    public override string Ploeh()
    {
        return "Ndøh";
    }
}

A dynamic mock library can do the same, and the same is true for abstract methods. In this case, the base class' constructor will be invoked, because that's how .NET works.

However, you can't write code that overrides a non-virtual or internal member, and neither can dynamic mocks. They can only do what you can do by hand.

A piece of advice, though: Only unit test internal members through your public API.

Caveat: The above description is true for most dynamic mocks with the exception of TypeMock, which is different and... scary.