With Integration tests a common scenario comes into picture. Integration tests always depend on system components e.g. Database, Services, etc. If the Integration test is failing unexpectedly then it might require to debug the test including that component which are usually part of the same VS solution. Recently I came across this SO question. Question seemed legit because while debugging a test, attaching a process again and again to debug becomes really annoying.
To make dependent components like services, I usually invoke the process to launch e.g. ConsoleApplication project in same solution. Its like simply add a helper class to invoke the process.
- internal class ProcessInvoker
- {
-
-
-
- public static void InvokeDummyService()
- {
- var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-
-
- ProcessStartInfo info = newProcessStartInfo(Path.Combine(path, "DummyService.exe"));
-
-
- info.UseShellExecute = true;
- info.WorkingDirectory = path;
-
-
- var process = Process.Start(info);
-
- AttachDebugger.ToProcess(process.Id);
- }
-
-
-
-
-
- public static void KillDummyService()
- {
- Process.GetProcessesByName("DummyService").ToList().ForEach(x => x.Kill());
- }
- }
viewrawProcessInvoker.cs hosted with ❤ by
GitHub Now in the TestInitialize and TestCleanup methods (I’m using NUnit in this sample code so it would be TestFixtureSetup and TextFixtureTearDown methods) I would start the process and kill the respective process. Here the Integration test is a sample from one of my Github project
WCFDynamicProxy. In this Test I’ll be invoking a Dummy service instance hosted in a Console application. Then completion of test just kill the process.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
- using SampleService.Contracts;
- using WcfDynamicProxy.Tests.Helper;
- using NUnit.Framework;
-
-
- namespace WcfDynamicProxy.Tests.Tests
- {
-
-
-
-
- [TestFixture]
- public class CustomClientProxyFactoryTest
- {
-
-
-
- [TestFixtureSetUp]
- public void Init()
- {
- ServiceHostProcessInvoker.InvokeDummyService();
- }
-
-
-
-
-
- [TestFixtureTearDown]
- public void TearDown()
- {
- ServiceHostProcessInvoker.KillDummyService();
- }
-
-
-
-
-
-
- [Test]
- public void GetDefaultWCFClientProxy_RequestingProxyClientGeneration_Succeeds()
- {
- var proxy = DynamicProxyWrapperFactory.GetProxyInstanceByInterface < ITestService > ("TestService");
-
-
- var result = proxy.SayHello();
-
-
- Assert.IsNotNull(result, "The result was null.");
- Assert.IsNotEmpty(result, "The result was empty.");
- }
- }
- }
viewrawWCFDynamicProxyTest.cs hosted with ❤ by
GitHub Now, comes the part to attach this process for debugging. This is pretty tricky one. I found a VS
Addin from Visual studio team to attach 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 have done a little customization to get the process Id as argument:
- 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(intdwCallType, IntPtrhTaskCaller, intdwTickCount, IntPtrlpInterfaceInfo);
-
-
- [PreserveSig]
- int RetryRejectedCall(IntPtrhTaskCallee, intdwTickCount, intdwRejectType);
-
-
- [PreserveSig]
- int MessagePending(IntPtrhTaskCallee, intdwTickCount, intdwPendingType);
- }
-
-
- public class MessageFilter: IOleMessageFilter
- {
- private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
-
-
- int IOleMessageFilter.HandleInComingCall(intdwCallType, IntPtrhTaskCaller, intdwTickCount, IntPtrlpInterfaceInfo)
- {
- return Handled;
- }
-
-
- int IOleMessageFilter.RetryRejectedCall(IntPtrhTaskCallee, intdwTickCount, intdwRejectType)
- {
- returndwRejectType == RetryAllowed ? Retry : Cancel;
- }
-
-
- int IOleMessageFilter.MessagePending(IntPtrhTaskCallee, intdwTickCount, intdwPendingType)
- {
- returnWaitAndDispatch;
- }
-
-
- public static void Register()
- {
- CoRegisterMessageFilter(newMessageFilter());
- }
-
-
- public static void Revoke()
- {
- CoRegisterMessageFilter(null);
- }
-
-
- private static void CoRegisterMessageFilter(IOleMessageFilternewFilter)
- {
- IOleMessageFilteroldFilter;
- CoRegisterMessageFilter(newFilter, out oldFilter);
- }
-
-
- [DllImport("Ole32.dll")]
- private static extern intCoRegisterMessageFilter(IOleMessageFilternewFilter, outIOleMessageFilteroldFilter);
- }
-
-
- public static class AttachDebugger
- {
- public static void ToProcess(intprocessId)
- {
- MessageFilter.Register();
- var process = GetProcess(processId);
- if (process != null)
- {
- process.Attach();
- Console.WriteLine("Attached to {0}", process.Name);
- }
- MessageFilter.Revoke();
- }
- private static Process GetProcess(intprocessID)
- {
- var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.12.0");
- var processes = dte.Debugger.LocalProcesses.OfType < Process > ();
- returnprocesses.SingleOrDefault(x => x.ProcessID == processID);
- }
- }
- }
viewrawAttachDebugger.cs hosted with ❤ by
GitHub Note: You need to add the VS automation library EnvDTE from AddReference ->Extensions. Also the VS version might be different. I was using Visual Studio 2013 so it’s ‘VisualStudio.DTE.12.0’. Change it as per your need.
Now in the ProcessInvoker class add the call to AttachDebugger utility class after the process launch statement.
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. You can verify it from menu –> Debug, Attach to process, then Locate the process expected to be attached.
Checkout the working code
here specially
WcfDynamicProxy.Tests in the solution.