Hans Rudel
Hans Rudel

Reputation: 3611

Instance of an object created in a method. How to mock that instance?

I have a method in my Presenter which creates a class which contains all the user inputs, called UserInputEntity. It implements the interface IUserInputEntity. I currently have it declared as a local variable, of type UserInputEntity, and thus can't mock it in the following method (simplified for brevity):

public void CompletionReportNotifier(object sender, VerificationStatusEventArgs e)
{
    _view.PermanentCsvFileVerificationCancellation = null;

    string logMessage;
    bool inputsVisible = false;

    //Mocking inputs.NumberOfErrorsFound??
    if (e.CarriedOutToCompletion != true || inputs.NumberOfErrorsFound > 0)
    {
        inputsVisible = true;
        _view.VerificationCompleted = false;
        logMessage = "failed to complete operation";
    }
    else
    {
        _view.VerificationCompleted = true;
        logMessage = "Completed operation";
    }
    _view.UIUpdate(logMessage, inputsVisible);
}

What is the most appropriate way to get around this? The only possible solution I can think of is to declare another method which only calls the entity classes constructor, and returns an IUserInputEntity. I would then change the declaration of inputs, in the presenter, to type IUserInputEntity. Would this be suitable or is there a better way?

Below is a copy of the method where the instance of inputs is created currently (simplified):

private void DataVerification(Object sender, EventArgs e)
{
    if (_view.VerifyingData != true)
    {
        inputs = new UserInputEntity(_view.DataTypeInputs, _view.ColumnNameInputs, _view.InitialRow, _view.FinalRow, _view.CurrencyPair, _view.CsvFilePath, _view.ErrorLogFilePath);

        // ...

        verification.VerifyDataTypesAsync();     
    }
    else
    {
        _view.PermanentCsvFileVerificationCancellation.Cancel();
    } 
}

Upvotes: 1

Views: 4464

Answers (3)

Michael Lloyd Lee mlk
Michael Lloyd Lee mlk

Reputation: 14661

Instance of an object created in a method. How to mock that instance?

That is what factories or providers are for. Ideally the factory/provider should be injected allowing you to fix what would be returned.

This assumes that the new is required. If not (i.e. it is a stateless service) then you should simply inject that service.

The final option, and not one to ignore is: Use the real damn object. If the logic contained in the collaborator is not complex and it does not have any dependencies and the object is fairly dumb, well tested and simple to set up not just using it will be a bigger pain than mocking it out.

Upvotes: 0

Phil Patterson
Phil Patterson

Reputation: 1262

You are going to have to mock a dependency at some point here. It's very difficult to say if the only wrong coupling in this example is the IUserInputEntity being instantiated in the view directly or if the _view coupling is also in error. The example doesn't show if _view implements an interface or not, but it should. In the MVP pattern the presenter mediates between the view and the model.

If the view is responsible for collecting the user input, then you need to mock a method on the view that returns an IUserInputEntity so that the presenter has access to it. The data validation I would argue needs to be passed to a model class (use the example YYY uses in his answer). Now the model can validate on the input from any type of IView.

So update your view to take the IUserInputEntity as in YYY's answer, and update the presenter to use DI to take an [IYourViewInterface] to simulate different types of output from the view. Use these interfaces in your presenter and have your view and model classes take dependencies in their constructors to maximize the testability of your application.

Upvotes: 1

tmesser
tmesser

Reputation: 7666

When following a dependency injection pattern, any usage of the keyword 'new' should be met with extreme caution and trepidation. Chances are fantastic it is exactly one of those dependencies that should be injected. This is definitely one of those times.

The real solution here is to add IUserInputEntity to your constructor and abstract it away for your inversion of control container (StructureMap, NInject, etc) to inject automatically. In structuremap, this would look a little like this:

public class DependencyRegistry : Registry
{
    public DependencyRegistry()
    {
        For<IUserInputEntity>().Use<UserInputEntity>();
    }
}

With the class usage:

public MyClass(IUserInputEntity userInputEntity)
{
   _userInputEntity = userInputEntity;
}

You may then set properties as you know them or use them freely in your concrete class. In your test, it will look something like this (presuming NUnit and RhinoMocks):

[Test]
public void MyTest()
{
   var mockEntity = MockRepository.GenerateMock<IUserInputEntity>();

   var testedClass = new MyClass(mockEntity);
}

At this point you may do whatever you please to train the mock entity using the various control methods provided with RhinoMocks, or whatever your mocking framework is. The important thing to note here is that you are not passing in a concrete implementation of UserInputEntity that functions how you programmed a concrete to function. You are passing in a mock that will do exactly what you tell it to do, and nothing more.

Upvotes: 1

Related Questions