2016-01-28 26 views
2

我使用NUnit/MSTest编写集成测试,仅仅是因为它更简单。测试需要在同一解决方案中与TCP服务器通信,并且它想要调试测试和TCP服务器。有没有办法在调试模式下从解决方案启动项目(控制台应用程序)并同时调试测试方法?无论我尝试VS不会让我。在同一解决方案中针对服务器运行集成测试

回答

3

编写集成测试时,这是一种常见的情况。集成测试依赖于另一项服务的启动和运行。为了处理它,我通常会调用这个过程来启动例如ConsoleApplication项目在相同的解决方案中。只需添加一个助手类来调用该过程。

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()); 
    } 
} 

现在在TestInitialize和TestCleanup方法中,我将启动进程并终止相应的进程。

/// <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(); 
    } 

现在,来附加这个过程进行调试的部分。这非常棘手。我发现了一个来自Visual Studio团队的VS Addin来自动将子进程附加到当前调试器,但它似乎只适用于“f5”调试。

然后我发现this SO post它真的非常令人惊讶。我在这里张贴的完整代码形式的回答很少定制:

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); 
     } 
    } 
} 

注意:您需要从AddReference添加的VS自动化库EnvDTE - >一些推广

现在在ProcessInvoker类添加在进程启动语句后调用AttachDebugger实用程序类。

var process = Process.Start(info); 
// Add this after invoking process. 
AttachDebugger.ToProcess(process.Id); 

当我启动调试测试它像魅力工作。该进程被调用,附加到VS并能够调试其他进程代码。

结帐工作解决方案here。特别是WcfDynamicProxy.Tests的解决方案。我使用Nunit在那里写集成测试。

+0

这太棒了!您是否知道我是否可以以同样的方式启动IISExpress调试?我想我可以在控制台应用程序中启动自己的主机。 –

+1

您可以附加IISExpress.exe进程来调试托管项目。但是,如果你想启动一个具有特定项目的IISExpress,请按照[此链接](http://www.iis.net/learn/extensions/using-iis-express/running-iis-express-from-the-command-线)。只需使用适当的命令行选项调用IISExpress进程,并通过AttachDebugger类附加它。 – vendettamit

0

一次调试两个程序是不可能的,但为什么您需要在调试模式下运行控制台应用程序?只需在不进行调试的情况下启动它,然后在调试模式下启动集成测试 - 如果您想在某个时刻调试控制台应用程序而不是测试方法,则可以启动Visual Studio的第二个实例,并将调试程序附加到控制台应用程序进程并从那里调试。

提示您也可以附加调试器,以编程方式从代码中调用Debuger.Launch()

相关问题