Here is how I got xUnit working with my Outlook VSTO addin project in Visual Studio 2019 (Community Edition).
Step 1: Create Outlook VSTO Add-in
Remember the Framework you select. I always use .NET Framework 4.7.2 (we will need that info for xUnit later):
![![[Pasted image 20230314161840.png]]](../../images/3824028797.webp)
Step 2: Install xUnit NuGet Packages
Right-click the solution, and select "Manage NuGet Packages for Solution..."
Click the Browse tab and search for xUnit. Install "xunit" by Jnewkirk etc:
![![[Pasted image 20230314154205.png]]](../../images/3810593912.webp)
Installing xUnit will also install its dependencies:
![![[Pasted image 20230314154318.png]]](../../images/3818130533.webp)
Sometimes (not always) Visual Studio glitches out and loses its reference to the "mscorlib" when I install NuGet packages into a VSTO add-in project:
![![[Pasted image 20230314154726.png]]](../../images/3805416547.webp)
If that happens, just quit Visual Studio and reload the solution. That seems to always do the trick for me. If you try to add the mscorlib assembly, you will get an error stating that it cannot be added because it is automatically added by the Build system.... weird.
Back to the NuGet package manager.
To get your tests to properly show in the "Test Explorer" window in Visual Studio, you will also need to add BOTH:
xunit.runner.visualstudio
and
xunit.runner.console
![![[Pasted image 20230314155525.png]]](../../images/3783396417.webp)
Step 3: Create xUnit Test Project
Right-click on your solution, and select "Add a new project".
Do a search for xUnit:
![![[Pasted image 20230314160026.png]]](../../images/3845983416.webp)
VERY VERY IMPORTANT
As discussed in the xUnit documentation, there is only a single xUnit template for Visual Studio, which is labeled ".NET Core". So at this screen, we have no choice but to select the .NET Core version, even though we are using .NET Framework 4.7.2 (we will change the target framework manually in the next step):
![![[Pasted image 20230314161609.png]]](../../images/3822193817.webp)
On the next screen leave it at ".NET Core 3.1(Out of Support)" and click "Create":
![![[Pasted image 20230314162027.png]]](../../images/3836218543.webp)
That should result in the xUnit test project being created along with a template "UnitTest1" class file.
Step 4: "Edit Project File" for the test project
As discussed in the xUnit documentation, we now need to edit the Project File for the test project, to target .NET Framework 4.7.2 (or whatever framework you picked in Step 1) instead of .NET Core.
Right-click on the test project, and select "Edit Project File":
![![[Pasted image 20230314162751.png]]](../../images/3837988021.webp)
An XML document should open in the text editor that looks like this:
![![[Pasted image 20230314162905.png]]](../../images/3822390393.webp)
Change the entry between the "TargeFramework" tags to read:
net472
Like so:
![![[Pasted image 20230314163157.png]]](../../images/3837988016.webp)
Step 5: Add needed Dependencies to the test project
PROBABLY MOST IMPORTANT
There are several Dependencies you need to add to the xUnit test project, in order for it to recognize your Outlook Addin-related objects. Some of these you need to "Browse" to find (see below) after you right-click on Dependencies and select "Add Assembly Reference":
Assemblies:
Microsoft.CSharp
Microsoft.Office.Tools.Common.v4.0.Utilities
Microsoft.Office.Tools.Outlook.v4.0.Utilities
COM:
Interop.Microsoft.Office.Core
Interop.Microsoft.Office.Interop.Outlook
Projects:
YourAddInProjectName
The following table lists where to find each of the above using the Reference Manager window:
Item |
Location |
Microsoft.CSharp |
Under "Assemblies" in the Reference Manager window |
Microsoft.Office.Tools.Common.v4.0.Utilities |
Click "Browse" then navigate to the "bin/Debug" folder for your Addin project. |
Microsoft.Office.Tools.Outlook.v4.0.Utilities |
Click "Browse" then navigate to the "bin/Debug" folder for your Addin project. |
Interop.Microsoft.Office.Core |
Under "COM" in the Reference Manager window, BUT LISTED AS: "Microsoft Office 16.0 Object Library" |
Interop.Microsoft.Office.Interop.Outlook |
Under "COM" in the Reference Manager window, BUT LISTED AS: "Microsoft Outlook 16.0 Object Library" |
YourAddInProjectName |
Under "Projects" in the Reference Manager window |
To state the obvious -- because I forget to do this sometimes -- you need to make sure to actually tick the checkbox next to each of the above in the Reference Manager window and then click "OK" (you can't just select the line with the item and click OK, you have to actually tick the box).
Step 6: Draft some code to test
To "test" that testing works through xUnit (pun intended), you can add a simple GetTestString method in the "ThisAddIn" class like so:
public partial class ThisAddIn
{
public static string TestGetString()
{
return "Hello World!";
}
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
// Note: Outlook no longer raises this event. If you have code that
// must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
You can also do a simple "test" test by adding a TestMailBoxWrapper class with an IsMailItemFolder method (either in the same text file as ThisAddIn or in a separate file):
public class TestMailBoxWrapper
{
public Outlook.Folder MyFolder { get; set; }
public TestMailBoxWrapper(Outlook.MAPIFolder myMapiFolder)
{
MyFolder = (Folder)myMapiFolder;
}
public bool IsMailItemFolder()
{
return MyFolder.DefaultItemType == Outlook.OlItemType.olMailItem;
}
}
Step 7: Draft some "test" unit tests
Here are a couple of "test" unit tests for the above sample code, which you could add to your UnitTest1 class in your xUnit project.
Make sure to add a "using" statement at the top of the file with the name of your add-in project!
(Please note that I chose the unfortunately confusing name of "OutlookAddInUnitSanityTest" for my addin project, which is almost identical to my equally ill-chosen xUnit project name "OutlookAddInSanityTests" -- sorry about that, but too far along in this answer to change those now.)
Also a good idea to add "using" statements for Outlook and Office, Linq, Reflection, etc. as per the example below:
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Xunit;
using OutlookAddInUnitSanityTest;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
namespace OutlookAddInSanityTests
{
public class UnitTest1
{
[Fact]
public void CanFindMethodInThisAddInClass()
{
var sut = OutlookAddInUnitSanityTest.ThisAddIn.TestGetString();
Assert.Equal("Hello World!", sut);
}
[Fact]
public void CanFindWrapperClass()
{
// Arrange
// The following is based on:
// https://learn.microsoft.com/en-us/office/client-developer/outlook/pia/how-to-get-and-log-on-to-an-instance-of-outlook
Outlook.Application application = null;
// Check whether there is an Outlook process running.
if (Process.GetProcessesByName("OUTLOOK").Count() > 0)
{
// If so, use the GetActiveObject method to obtain the process and cast it to an Application object.
application = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
}
else
{
// If not, create a new instance of Outlook and sign in to the default profile.
application = new Outlook.Application();
Outlook.NameSpace nameSpace = application.GetNamespace("MAPI");
nameSpace.Logon("", "", Missing.Value, Missing.Value);
nameSpace = null;
}
var myOutlookNameSpace = application.GetNamespace("MAPI");
var myInbox = myOutlookNameSpace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
TestMailBoxWrapper myTestWrapper = new TestMailBoxWrapper(myInbox);
// Act
bool sut = myTestWrapper.IsMailItemFolder();
// Assert
Assert.True(sut);
}
}
}
Step 8: Run your tests
Bring up the Test Explorer window under the View menu, and select the Play button to run the tests. After a few moments you should see green checkmarks (I like to dock the Test Explorer in the lower-right):
![![[Pasted image 20230314181354.png]]](../../images/3841395868.webp)
The above tests are set up to be run whether or not Outlook is running. Just be aware that you cannot run the tests while you are running Outlook to debug the AddIn. You may run the tests without Outlook running, in which case Outlook will launch and then close when the test is done.
Or, launch Outlook manually without hitting the Debug Start button
, and then launch the tests in Visual Studio. I've found that the speed of the tests is greatly increased doing it that way.
Step 9: Keep writing tests and code for your VSTO Outlook Add-In
All of the above works without having to create any classes or methods with COM attributes, or having to set "Register for COM interop" in the Build settings, etc. etc.
Even all the xUnit CodeLens decorators show up nicely on the methods in my add-in classes:
![![[Pasted image 20230314182131.png]]](../../images/3809021032.webp)
![![[Pasted image 20230314182207.png]]](../../images/3831565470.webp)
I hope this helps anyone else struggling with getting xUnit to work with Outlook VSTO add-ins.