Mocking method results

2020-06-03 05:45发布

问题:

I'm trying to find a way to fake the result of a method called from within another method.

I have a "LoadData" method which calls a separate helper to get some data and then it will transform it (I'm interested in testing the transformed result).

So I have code like this:

public class MyClass(){
  public void LoadData(){
    SomeProperty = Helper.GetSomeData();
 }
 public object SomeProperty {get;set;}
}

I want to have a known result from the Helper.GetSomeData() method. Can I use a mocking framework (I've got fairly limited experience with Rhino Mocks but am open to anything) to force an expected result? If so, how?

*Edit - yeah as expected I couldn't achieve the hack I wanted, I'll have to work out a better way to set up the data.

回答1:

As far as I know, you should create an interface or a base abstract class for the Helper object. With Rhino Mocks you can then return the value you want.

Alternatively, you can add an overload for LoadData that accepts as parameters the data that you normally retrieve from the Helper object. This might even be easier.



回答2:

You have a problem there. I don't know if thats a simplified scenario of your code, but if the Helper class is used that way, then your code is not testable. First, the Helper class is used directly, so you can't replace it with a mock. Second, you're calling a static method. I don't know about C#, but in Java you can't override static methods.

You'll have to do some refactoring to be able to inject a mock object with a dummy GetSomeData() method.

In this simplified version of your code is difficult to give you a straight answer. You have some options:

  • Create an interface for the Helper class and provide a way for the client to inject the Helper implementation to the MyClass class. But if Helper is just really a utility class it doesn't make much sense.
  • Create a protected method in MyClass called getSomeData and make it only call Helper.LoadSomeData. Then replace the call to Helper.LoadSomeData in LoadData with for getSomeData. Now you can mock the getSomeData method to return the dummy value.

Beware of simply creating an interface to Helper class and inject it via method. This can expose implementation details. Why a client should provide an implementation of a utility class to call a simple operation? This will increase the complexity of MyClass clients.



回答3:

I would recommend converting what you have into something like this:

public class MyClass()
{
    private IHelper _helper;

    public MyClass()
    {
        //Default constructor normal code would use.
        this._helper = new Helper();
    }

    public MyClass(IHelper helper)
    {
        if(helper == null)
        {
            throw new NullException(); //I forget the exact name but you get my drift ;)
        }
        this._helper = helper;
    }

    public void LoadData()
    {
        SomeProperty = this._helper.GetSomeData();
    }
    public object SomeProperty {get;set;}
}

Now your class supports what is known as dependency injection. This allows you to inject the implementation of the helper class and it ensures that your class need only depend on the interface. When you mock this know you just create a mock that uses the IHelper interface and pass it in to the constructor and your class will use that as though it is the real Helper class.

Now if you're stuck using the Helper class as a static class then I would suggest that you use a proxy/adapter pattern and wrap the static class with another class that supports the IHelper interface (that you will also need to create).

If at some point you want to take this a step further you could completely remove the default Helper implementation from the revised class and use IoC (Inversion of Control) containers. If thiis is new to you though, I would recommend focusing first on the fundamentals of why all of this extra hassle is worth while (it is IMHO).

Your unit tests will look something like this psuedo-code:

public Amazing_Mocking_Test()
{
    //Mock object setup
    MockObject mockery = new MockObject();
    IHelper myMock = (IHelper)mockery.createMockObject<IHelper>();
    mockery.On(myMock).Expect("GetSomeData").WithNoArguments().Return(Anything);

    //The actual test
    MyClass testClass = new MyClass(myMock);
    testClass.LoadData();

    //Ensure the mock had all of it's expectations met.
    mockery.VerifyExpectations();
}

Feel free to comment if you have any questions. (By the way I have no clue if this code all works I just typed it in my browser, I'm mainly illustrating the concepts).



回答4:

You might want to look into Typemock Isolator, which can "fake" method calls without forcing you to refactor your code. I am a dev in that company, but the solution is viable if you would want to choose not to change your design (or forced not to change it for testability) it's at www.Typemock.com

Roy blog: ISerializable.com



回答5:

Yes, a mocking framework is exactly what you're looking for. You can record / arrange how you want certain mocked out / stubbed classes to return.

Rhino Mocks, Typemock, and Moq are all good options for doing this.

Steven Walther's post on using Rhino Mocks helped me a lot when I first started playing with Rhino Mocks.



回答6:

I would try something like this:

public class MyClass(){
  public void LoadData(IHelper helper){
    SomeProperty = helper.GetSomeData();
 }

This way you can mock up the helper class using for example MOQ.