Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing of .NET Add-In for Microsoft Office

Has anyone got any suggestions for unit testing a Managed Application Add-In for Office? I'm using NUnit but I had the same issues with MSTest.

The problem is that there is a .NET assembly loaded inside the Office application (in my case, Word) and I need a reference to that instance of the .NET assembly. I can't just instantiate the object because it wouldn't then have an instance of Word to do things to.

Now, I can use the Application.COMAddIns("Name of addin").Object interface to get a reference, but that gets me a COM object that is returned through the RequestComAddInAutomationService. My solution so far is that for that object to have proxy methods for every method in the real .NET object that I want to test (all set under conditional-compilation so they disappear in the released version).

The COM object (a VB.NET class) actually has a reference to the instance of the real add-in, but I tried just returning that to NUnit and I got a nice p/Invoke error:

System.Runtime.Remoting.RemotingException : This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server. at System.Runtime.Remoting.Proxies.RemotingProxy.InternalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType) at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(IMessage reqMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)

I tried making the main add-in COM visible and the error changes:

System.InvalidOperationException : Operation is not valid due to the current state of the object. at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)

While I have a work-around, it's messy and puts lots of test code in the real project instead of the test project - which isn't really the way NUnit is meant to work.

like image 869
Richard Gadsden Avatar asked Sep 24 '08 11:09

Richard Gadsden


People also ask

Which plugin can be used for .NET unit tests?

Jenkins provides an out of box functionality for Junit, and provides a host of plugins for unit testing for other technologies, an example being MSTest for . Net Unit tests. If you go to the link https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin it will give the list of Unit Testing plugins available.

What is .NET unit testing?

Unit testing breaks the program down into the smallest bit of code, usually function-level, and ensures that the function returns the value one expects. By using a unit testing framework, the unit tests become a separate entity which can then run automated tests on the program as it is being built.

What is Microsoft unit testing?

In this article It's called unit testing because you break down the functionality of your program into discrete testable behaviors that you can test as individual units. Visual Studio Test Explorer provides a flexible and efficient way to run your unit tests and view their results in Visual Studio.


2 Answers

This is how I resolved it.

  1. Just about everything in my add-in runs from the Click method of a button in the UI. I have changed all those Click methods to consist only of a simple, parameterless call.

  2. I then created a new file (Partial Class) called EntryPoint that had lots of very short Friend Subs, each of which was usually one or two calls to parameterised worker functions, so that all the Click methods just called into this file. So, for example, there's a function that opens a standard document and calls a "save as" into our DMS. The function takes a parameter of which document to open, and there are a couple of dozen standard documents that we use.

So I have

Private Sub btnMemo_Click(ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, ByRef CancelDefault As Boolean) Handles btnMemo.Click
    DocMemo()
End Sub

in the ThisAddin and then

Friend Sub DocMemo()
    OpenDocByNumber("Prec", 8862, 1)
End Sub

in my new EntryPoints file.

  1. I add a new AddInUtilities file which has

    Public Interface IAddInUtilities

#If DEBUG Then

Sub DocMemo()

#End If

End Interface


Public Class AddInUtilities
    Implements IAddInUtilities
    Private Addin as ThisAddIn

#If DEBUG Then

Public Sub DocMemo() Implements IAddInUtilities.DocMemo
    Addin.DocMemo()
End Sub

#End If

 Friend Sub New(ByRef theAddin as ThisAddIn)
     Addin=theAddin
 End Sub
 End Class
  1. I go to the ThisAddIn file and add in

    Private utilities As AddInUtilities

    Protected Overrides Function RequestComAddInAutomationService() As Object If utilities Is Nothing Then utilities = New AddInUtilities(Me) End If Return utilities End Function

And now it's possible to test the DocMemo() function in EntryPoints using NUnit, something like this:

<TestFixture()> Public Class Numbering

Private appWord As Word.Application
Private objMacros As Object

<TestFixtureSetUp()> Public Sub LaunchWord()
    appWord = New Word.Application
    appWord.Visible = True

    Dim AddIn As COMAddIn = Nothing
    Dim AddInUtilities As IAddInUtilities
    For Each tempAddin As COMAddIn In appWord.COMAddIns
        If tempAddin.Description = "CobbettsMacrosVsto" Then
            AddIn = tempAddin
        End If
    Next
    AddInUtilities = AddIn.Object
    objMacros = AddInUtilities.TestObject


End Sub

<Test()> Public Sub DocMemo()


    objMacros.DocMemo()
End Sub

<TestFixtureTearDown()> Public Sub TearDown()
    appWord.Quit(False)
End Sub

End Class

The only thing you can't then unit test are the actual Click events, because you're calling into EntryPoints in a different way, ie through the RequestComAddInAutomationService interface rather than through the event handlers.

But it works!

like image 107
Richard Gadsden Avatar answered Sep 27 '22 19:09

Richard Gadsden


Consider the various mocking frameworks NMock, RhinoMocks, etc. to fake the behavior of Office in your tests.

like image 36
Lou Franco Avatar answered Sep 27 '22 19:09

Lou Franco