在方法中创建的对象的实例。如何模拟该实例?

时间:2012-08-27 14:54:49

标签: c# unit-testing .net-4.0 moq mvp

我的Presenter中有一个方法,它创建一个包含所有用户输入的类,名为UserInputEntity。它实现了接口IUserInputEntity。我目前已将其声明为UserInputEntity类型的局部变量,因此无法在以下方法中进行模拟(为简洁起见而简化):

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);
}

最合适的解决方法是什么?我能想到的唯一可能的解决方案是声明另一个只调用实体类构造函数的方法,并返回IUserInputEntity。然后,我会在演示者中更改inputs的声明,以键入IUserInputEntity。这是合适的还是有更好的方法?

以下是当前(简化)创建inputs实例的方法的副本:

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();
    } 
}

3 个答案:

答案 0 :(得分:1)

当遵循依赖注入模式时,应该极其谨慎和惶恐地使用关键字“new”。机会很棒,它恰好是应该注入的依赖项之一。这绝对是其中之一。

这里真正的解决方案是将IUserInputEntity添加到构造函数中并将其抽象出来以便自动反转控件容器(StructureMap,NInject等)。在structuremap中,这看起来有点像这样:

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

使用课程:

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

然后您可以根据需要设置属性,也可以在具体类中自由使用它们。在你的测试中,它看起来像这样(假设NUnit和RhinoMocks):

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

   var testedClass = new MyClass(mockEntity);
}

此时,您可以随心所欲地使用RhinoMocks提供的各种控制方法或任何模拟框架来训练模拟实体。这里需要注意的重要一点是,你没有传递UserInputEntity的具体实现,它具有如何编写具体功能的功能。你正在传递一个模拟器,它将完全按照你所说的去做,仅此而已。

答案 1 :(得分:1)

你将不得不在某处嘲笑依赖。很难说这个例子中唯一错误的耦合是否是直接在视图中实例化的IUserInputEntity,或者_view耦合是否也是错误的。该示例未显示_view是否实现了接口,但它应该如此。在MVP模式中,演示者在视图和模型之间进行调解。

如果视图负责收集用户输入,那么您需要在视图上模拟一个返回IUserInputEntity的方法,以便演示者可以访问它。我认为需要将数据验证传递给模型类(使用YYY在其答案中使用的示例)。现在,模型可以验证来自任何类型的IView的输入。

因此更新您的视图以获取YYY答案中的IUserInputEntity,并更新演示者以使用DI从[IYourViewInterface]模拟视图中的不同类型的输出。在演示者中使用这些接口,让视图和模型类在其构造函数中采用依赖关系,以最大限度地提高应用程序的可测试性。

答案 2 :(得分:0)

  

在方法中创建的对象的实例。如何模拟该实例?

这就是工厂或供应商的用途。理想情况下,应该注入工厂/供应商,允许您修复将要返回的内容。

这假定需要new。如果没有(即它是无国籍服务),那么你应该只是注入该服务。

最后的选择,而不是忽略的是:使用真正该死的对象。 如果协作者中包含的逻辑并不复杂,并且它没有任何依赖关系,并且对象相当愚蠢,经过良好测试并且设置简单而不仅仅使用它将比嘲弄它更大的痛苦。