Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq a MEF Import?

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

And a Test to test this (seperate Dll - obviously)

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

This fails as attempting to call 'MyService' gives me null. I've then tried:

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

with 'MyService' declared as:

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

But still no joy, am I missing something - is this even possible?

I'm using SL3, MEF Preview 9 and MOQ.

Any help appreciated!

Cheers

Chris

like image 241
Charlotte Skardon Avatar asked Jul 02 '10 15:07

Charlotte Skardon


3 Answers

Your class should look like this:

public class A 
{
    private readonly IMyService _myService;

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

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

And your test should look like this:

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

The fact that you use MEF should not be important in unit tests. MEF only comes into play when wiring many components together, which is exactly the opposite of what happens in a unit test. A unit test is by definition a test of a component in isolation.

Edit: if you prefer property injection, then your class doesn't need a constructor and the arrange part in your unit test should look like this instead:

    var myService = new Mock<IMyService>();
    var a = new A();
    a.MyService = myService.Object;
like image 168
Wim Coenen Avatar answered Sep 28 '22 17:09

Wim Coenen


Where you've added [Export] to your IMyService instance, have you actually added that to the composition container? If not, it won't take part in composition. To add a mocked object to the container, do the following:

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

Or just:

container.ComposeExportedValue(mock.Object); // type inference.

Doing this before you've created an instance of A will enable it to be composed inside your A instance.

like image 41
Matthew Abbott Avatar answered Sep 28 '22 18:09

Matthew Abbott


You shouldn't be firing up MEF in your unit tests. Composition is way beyond unit test's scope, not dissimilar to an IoC container.

Insted, you should inject required dependencies manually:

[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());
  }
}
like image 34
Igor Zevaka Avatar answered Sep 28 '22 16:09

Igor Zevaka