2

I'm trying to create unit tests for an Excel VSTO add-in I've created, but I've ran into an incredibly mysterious issue that feels well beyond my experience.

In this case, I have a presenter:

public class Presenter
{
  private readonly Excel.Application Application;

  public Presenter(Excel.Application Application)
  {
    this.Application = Application;

    Application.WorkbookActivate += Application_WorkbookActivate;
  }

  private void Application_WorkbookActivate(Excel.Workbook Wb)
  {
    // logic to be tested
  }
}

My unit test is to verify that when WorkbookActivate is called, it performs a specific action, e.g.:

[Test]
public void TestLogicWhenWorkbookActivates()
{
  var mockApplication = new Mock<Excel.Application>();

  presenter = new Presenter(mockApplication.Object);

  // Act
  mockApplication.Raise(a => a.WorkbookActivate += null, (Excel.Workbook)null);

  // Assert
  // ...
}

Now, when I run this test, it fails when the event is added (which occurs in the presenter's constructor), throwing the following:

System.MissingMethodException : Error: Missing method 'instance void [ExcelAddIns.TestControl] Microsoft.Office.Interop.Excel.AppEvents_Event::add_WorkbookActivate(class Microsoft.Office.Interop.Excel.AppEvents_WorkbookActivateEventHandler)' from class 'Castle.Proxies.ApplicationProxy'.

My understanding, based on this related Stack Overflow post was that what I am performing is a third party callback, and that failed prior to Moq 4.0. I am using Moq 4.2.1402.2112.

So here's the weird part: in that Stack Overflow answer, the Moq bug report is mentioned, which possesses a unit test to test this very concept:

[Test]
public void InteropExcelTest()
{
  var mockAppExcel = new Mock<Excel.Application>();

  bool isDelegateCalled = false;

  mockAppExcel.Object.WorkbookActivate += delegate { isDelegateCalled = true; };

  mockAppExcel.Raise(ae => ae.WorkbookActivate += null, (Excel.Workbook)null);

  Assert.True(isDelegateCalled);
}

And this test, indeed, passes, which implies that my first test should be valid as it is properly handling the event. But what's even stranger is that the inclusion of this test into my unit test .cs file causes the previously failing test (TestLogicWhenWorkbookActivates) to pass!

These tests are completely independent. Why is the presence of the second causing the first to pass?

Community
  • 1
  • 1
Arclight
  • 493
  • 4
  • 18

1 Answers1

4

After some additional research and experimentation, I believe I have solved my problem. I'm not an expert on interop, so if I make any false conclusions here please feel free to edit or correct me.

I was working with two projects:

  • ExcelAddIns.TestControl
  • ExcelAddIns.TestControl.Tests

Both required VSTO references (including Microsoft.Office.Interop.Excel, among others). All VSTO references had Embed Interop Types set to true (which I believe is the default). Based on what I've read (see references below), this was the key to the problem, as it effectively separated the assemblies' embedded types, causing a clash when my Unit Test referenced the TestControl project.

Thus, my solution was to set Embed Interop Types to false for all VSTO assembly references in both projects. This caused the unit test to pass without the presence of the second test.

References:

Stack Overflow: Moq & Interop Types: works in VS2012, fails in VS2010?

Stack Overflow: What's the difference setting Embed Interop Types true and false in Visual Studio?

Daniel Cazzulino's Blog: Check your Embed Interop Types flag when doing Visual Studio extensibility work

Community
  • 1
  • 1
Arclight
  • 493
  • 4
  • 18