I write integration tests with NUnit/MSTest, just because it's easier. The tests need to talk to a TCP server in the same solution and it'd like to debug both the test and the TCP server. Is there any way to start the project (console app) from the solution in debug mode and debug a test method at the same time? Whatever I tried VS won't allow me.
2 Answers
This is a common scenario when writing integration tests. Integration tests are dependent on another service to be up and running. To deal with it I usually invoke the process to launch e.g. ConsoleApplication project in same solution. Simply Add a helper class to invoke the process.
internal class ProcessInvoker
{
/// <summary>
/// Invokes the host process for test service
/// </summary>
public static void InvokeDummyService()
{
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
ProcessStartInfo info = new ProcessStartInfo(Path.Combine(path, "DummyService.exe"));
info.UseShellExecute = true;
info.WorkingDirectory = path;
var process = Process.Start(info);
}
/// <summary>
/// Kills the process of service host
/// </summary>
public static void KillDummyService()
{
Process.GetProcessesByName("DummyService").ToList().ForEach(x => x.Kill());
}
}
Now in the TestInitialize and TestCleanup methods I would start the process and kill the respective process.
/// <summary>
/// Setup required before the tests of the fixture will run.
/// </summary>
[TestFixtureSetUp]
public void Init()
{
ProcessInvoker.InvokeDummyService();
}
/// <summary>
/// Tear down to perform clean when the execution is finished.
/// </summary>
[TestFixtureTearDown]
public void TearDown()
{
ProcessInvoker.KillDummyService();
}
Now, Comes the part to attach this process for debugging. This is pretty tricky one. I found an VS Addin from Visual studio team to attache child process automatically to current debugger but it seems it only works with "f5" debugging.
Then I found this SO post and It really worked amazingly. I'm posting the complete code form the answer here with little customization:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using EnvDTE;
namespace Common
{
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
public class MessageFilter : IOleMessageFilter
{
private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return Handled;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RetryAllowed ? Retry : Cancel;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return WaitAndDispatch;
}
public static void Register()
{
CoRegisterMessageFilter(new MessageFilter());
}
public static void Revoke()
{
CoRegisterMessageFilter(null);
}
private static void CoRegisterMessageFilter(IOleMessageFilter newFilter)
{
IOleMessageFilter oldFilter;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
public static class AttachDebugger
{
public static void ToProcess(int processId)
{
MessageFilter.Register();
var process = GetProcess(processId);
if (process != null)
{
process.Attach();
Console.WriteLine("Attached to {0}", process.Name);
}
MessageFilter.Revoke();
}
private static Process GetProcess(int processID)
{
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.12.0");
var processes = dte.Debugger.LocalProcesses.OfType<Process>();
return processes.SingleOrDefault(x => x.ProcessID == processID);
}
}
}
Note: You need to add the VS automation library EnvDTE
from AddReference -> Extentions
Now in the ProcessInvoker
class Add the call to AttachDebugger
utility class after the process launch statement.
var process = Process.Start(info);
// Add this after invoking process.
AttachDebugger.ToProcess(process.Id);
When I launched a test for debugging it worked like charm. The process was invoked, attached to VS and was able to debug other process code.
Checkout the working solution here. specially WcfDynamicProxy.Tests in the solution. I used Nunit to write Integration test there.

- 18,769
- 10
- 104
- 133

- 14,315
- 2
- 32
- 54
-
This is brilliant! Do you know by any chance if I could start IISExpress debugging the same way? I guess I could just start own self host in the console app. – Ivan G. Jan 29 '16 at 13:24
-
1you can attach the IISExpress.exe process to debug a hosted project. But if you want to start the IISExpress with a sepcific project follow [this link](http://www.iis.net/learn/extensions/using-iis-express/running-iis-express-from-the-command-line). Just invoke the IISExpress process with proper commandline options and attach it via AttachDebugger class. – vendettamit Jan 29 '16 at 14:47
Debugging two programs at once is not possible, but why you need to run Console app in debug mode? Just start it without debugging, then start Integration tests in debug mode - if you want to debug Console app at some point instead of test method you may start second instance of visual studio and attach debugger to Console app process and debug from there.
TIP You may also attach debugger to program programmatically calling Debuger.Launch() from code.

- 4,124
- 25
- 31