Moq一个MEF导入?

9

I have a class A which has the following:

public class A {
    [Import(typeof(IMyService)]
    public IMyService MyService { get; set; }

    public A() {
        CompositionInitializer.SatisfyImports(this);
    }

    public void DoWork() {
        //Blah
        MyService.DoIt();
        //Blah
    }
}

还有一个测试来测试这个(分离的Dll - 显然)

[TestMethod]
public void TestDoWork() {
    //Blah
    DoWork();
    //Assert assert
}

当我尝试调用“MyService”时,这个方法失败并返回null。为此,我尝试了以下方法:

[ClassInitialize]
public void InitialiseClass() {
    var myService = new Mock<IMyService>();
    MyService = myService.Object;
}

'MyService'声明为:

[Export(typeof(IMyService))]
public IMyService MyService { get; set; }

但仍然没有成功,我错过了什么吗?这种情况可能存在吗?

我正在使用SL3、MEF预览版9和MOQ。

非常感谢您的帮助!

谢谢

Chris

3个回答

7
你的类应该像这样:
```html

你的类应该像这样:

```
public class A 
{
    private readonly IMyService _myService;

    [ImportingConstructor]
    public A(IMyService myService)
    {
        _myService = myService;
    }

    public void DoWork() {
        //Blah
        _myService.DoIt();
        //Blah
    }
}

你的测试应该像这样:

你的测试应该像这样:

[TestMethod]
public void DoWork_invokes_IMyService_DoIt() 
{
    // arrange mock and system under test
    var myService = new Mock<IMyService>();
    var a = new A(myService.Object);

    // act    
    a.DoWork();

    // assert that DoIt() was invoked
    myService.Verify(x => x.DoIt());
}

在单元测试中,使用MEF并不重要。MEF只在连接多个组件时才起作用,而这恰恰与单元测试的定义相反。单元测试是对组件进行隔离测试的。

编辑: 如果您喜欢属性注入,则您的类不需要构造函数,您的单元测试中的准备部分应该像这样:

    var myService = new Mock<IMyService>();
    var a = new A();
    a.MyService = myService.Object;

好的,但是为什么我需要使用构造函数的导入器呢?在我实际的实现中,属性工作得很好,可能有一种方法可以模拟这些类型的导入吗? - Charlotte Skardon
1
@Chris:虽然MEF鼓励属性注入,但我更喜欢构造函数注入,因为这样编译器可以防止您创建缺少依赖项的对象。它还允许您使依赖字段为只读,这样您就不必考虑替换依赖项时会发生什么。 - Wim Coenen
我认为您做出了假设,即该属性具有公共的设置器。但是,如果您正在注入依赖项,那么就不需要公共的设置器。例如,我有一个将其视图注入的演示文稿,反之亦然。Presenter封装了视图,因此没有公共设置器。同样适用于视图,它的Presenter被注入并且没有公共设置器。 - IAbstract
@IAbstract:按定义来说,注入点应该是公共的。如果不是这样,你就要依赖于容器的魔法来填充组件的依赖关系。当你需要在容器之外(例如单元测试)或使用不支持该功能的不同容器时重用该组件时,这将导致问题。当你构建一个可重用的组件库并且你无法控制使用该库的不同应用程序时,这些问题就会变得明显。 - Wim Coenen
虽然我不同意“注入点应该默认为公共”,但如果注入点不是公共的话,可能会遇到困难。在我注入一个视图的演示者(并且视图被注入了它的演示者)的情况下,我使用了反射来“注入”模拟对象。虽然有这个要求有点麻烦,但我不需要为每个测试启动组合,并且我的视图保持封装。 - IAbstract
显示剩余2条评论

2

如果您将 [Export] 添加到了 IMyService 实例中,您是否确实将其添加到组合容器中了呢?如果没有,它就不会参与组合。要将模拟对象添加到容器中,请执行以下操作:

 container.ComposeExportedValue<IMyService>(mock.Object);

或者只需这样:
container.ComposeExportedValue(mock.Object); // type inference.

在创建A实例之前这样做将使其能够在A实例内部被组合。

我不需要将具体实现添加到容器中才能使其工作。但我明白你的意思。你在哪里看到容器呢? - Charlotte Skardon

1

在单元测试中不应该启动MEF。组合远远超出了单元测试的范围,与IoC容器并没有太大区别。

相反,您应该手动注入所需的依赖项:

[TestClass]
public class ATest {
  Mock<IMyService> myService ;
  [TestInitialize]
  public void InitialiseClass() {
    myService = new Mock<IMyService>();
  }

  [TestMethod]
  public void DoWorkShouldCallDoIt {
    A a = new A();
    a.MyService = myService.Object;
    a.DoWork();
    myService.Verify(m=>m.DoIt(), Times.Once());
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接