2008-11-04 2 views
25

Я написал несколько пользовательских задач MSBuild, которые хорошо работают и используются в нашем процессе сборки CruiseControl.NET.Единичный тест MSBuild Custom Task без «Задача была предпринята для регистрации до ее инициализации» error

Я изменяю его и хочу, чтобы модуль тестировал его, вызвав метод Execute() задачи.

Однако, если он встречает строку, содержащую

Log.LogMessage("some message here"); 

он бросает InvalidOperationException:

Задача попытался войти, прежде чем она была инициализирована. Сообщение было ...

Любые предложения? (В прошлом у меня в основном были проверены на предмет внутренних внутренних методов для моих задач, чтобы избежать таких проблем.)

+1

Я только что столкнулся с этим в некоторых пользовательских задачах для SO - ответ Бранстара был прав! Просто установите BuildEngine на вызываемую задачу. – 2009-01-20 04:46:14

ответ

24

Вы должны установить свойство .BuildEngine пользовательской задачи вы звоните.

Вы можете установить его в тот же BuildEngine, который использует ваша текущая задача, чтобы легко включать вывод.

Task myCustomTask = new CustomTask(); 
myCustomTask.BuildEngine = this.BuildEngine; 
myCustomTask.Execute(); 
+0

Это было именно то, что мне нужно - спасибо! – 2009-01-20 04:46:59

+12

Поскольку вопрос был в контексте модульного теста, я бы также добавил, что вы можете альтернативно установить свойство BuildEngine в объект mock/stub, который реализует интерфейс IBuildEngine. – 2010-05-12 19:16:49

7

Если вы внедрили интерфейс ITask, вам придется инициализировать класс журнала самостоятельно.

В противном случае вы должны просто наследовать от Task в Microsoft.Build.Utilities.dll , который реализует ITask и делает много работы ног для вас.

Здесь приведена справочная страница для создания пользовательской задачи, она объясняет довольно много.

Building a custom MSBuild task reference

Также стоит посмотреть это

How to debug a custom MSBuild task

Другое то, что вы могли бы разместить XML MSBuild вы используете для вызова вашей пользовательской задачи. Сам код, очевидно, будет наиболее полезен :-)

+0

+1 ... nice links – alexandrul 2010-05-19 10:26:46

14

Я обнаружил, что экземпляр журнала не работает, если задача не запущена внутри msbuild, поэтому я обычно переношу свои вызовы в журнал, а затем проверяю значение BuildEngine для определения, если я запускаю внутри msbuild. Как показано ниже.

Tim

private void LogFormat(string message, params object[] args) 
{ 
    if (this.BuildEngine != null) 
    { 
     this.Log.LogMessage(message, args); 
    } 
    else 
    { 
     Console.WriteLine(message, args); 
    } 
} 
7

комментарий @Kiff для макета/заглушки IBuildEngine - хорошая идея. Вот мой FakeBuildEngine. Приведены примеры C# и VB.NET.

VB.NET

Imports System 
Imports System.Collections.Generic 
Imports Microsoft.Build.Framework 

Public Class FakeBuildEngine 
    Implements IBuildEngine 

    // It's just a test helper so public fields is fine. 
    Public LogErrorEvents As New List(Of BuildErrorEventArgs) 
    Public LogMessageEvents As New List(Of BuildMessageEventArgs) 
    Public LogCustomEvents As New List(Of CustomBuildEventArgs) 
    Public LogWarningEvents As New List(Of BuildWarningEventArgs) 

    Public Function BuildProjectFile(
     projectFileName As String, 
     targetNames() As String, 
     globalProperties As System.Collections.IDictionary, 
     targetOutputs As System.Collections.IDictionary) As Boolean 
     Implements IBuildEngine.BuildProjectFile 

     Throw New NotImplementedException 

    End Function 

    Public ReadOnly Property ColumnNumberOfTaskNode As Integer 
     Implements IBuildEngine.ColumnNumberOfTaskNode 
     Get 
      Return 0 
     End Get 
    End Property 

    Public ReadOnly Property ContinueOnError As Boolean 
     Implements IBuildEngine.ContinueOnError 
     Get 
      Throw New NotImplementedException 
     End Get 
    End Property 

    Public ReadOnly Property LineNumberOfTaskNode As Integer 
     Implements IBuildEngine.LineNumberOfTaskNode 
     Get 
      Return 0 
     End Get 
    End Property 

    Public Sub LogCustomEvent(e As CustomBuildEventArgs) 
     Implements IBuildEngine.LogCustomEvent 
     LogCustomEvents.Add(e) 
    End Sub 

    Public Sub LogErrorEvent(e As BuildErrorEventArgs) 
     Implements IBuildEngine.LogErrorEvent 
     LogErrorEvents.Add(e) 
    End Sub 

    Public Sub LogMessageEvent(e As BuildMessageEventArgs) 
     Implements IBuildEngine.LogMessageEvent 
     LogMessageEvents.Add(e) 
    End Sub 

    Public Sub LogWarningEvent(e As BuildWarningEventArgs) 
     Implements IBuildEngine.LogWarningEvent 
     LogWarningEvents.Add(e) 
    End Sub 

    Public ReadOnly Property ProjectFileOfTaskNode As String 
     Implements IBuildEngine.ProjectFileOfTaskNode 
     Get 
      Return "fake ProjectFileOfTaskNode" 
     End Get 
    End Property 

End Class 

C#

using System; 
using System.Collections.Generic; 
using Microsoft.Build.Framework; 

public class FakeBuildEngine : IBuildEngine 
{ 

    // It's just a test helper so public fields is fine. 
    public List<BuildErrorEventArgs> LogErrorEvents = new List<BuildErrorEventArgs>(); 

    public List<BuildMessageEventArgs> LogMessageEvents = 
     new List<BuildMessageEventArgs>(); 

    public List<CustomBuildEventArgs> LogCustomEvents = 
     new List<CustomBuildEventArgs>(); 

    public List<BuildWarningEventArgs> LogWarningEvents = 
     new List<BuildWarningEventArgs>(); 

    public bool BuildProjectFile(
     string projectFileName, string[] targetNames, 
     System.Collections.IDictionary globalProperties, 
     System.Collections.IDictionary targetOutputs) 
    { 
     throw new NotImplementedException(); 
    } 

    public int ColumnNumberOfTaskNode 
    { 
     get { return 0; } 
    } 

    public bool ContinueOnError 
    { 
     get 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public int LineNumberOfTaskNode 
    { 
     get { return 0; } 
    } 

    public void LogCustomEvent(CustomBuildEventArgs e) 
    { 
     LogCustomEvents.Add(e); 
    } 

    public void LogErrorEvent(BuildErrorEventArgs e) 
    { 
     LogErrorEvents.Add(e); 
    } 

    public void LogMessageEvent(BuildMessageEventArgs e) 
    { 
     LogMessageEvents.Add(e); 
    } 

    public void LogWarningEvent(BuildWarningEventArgs e) 
    { 
     LogWarningEvents.Add(e); 
    } 

    public string ProjectFileOfTaskNode 
    { 
     get { return "fake ProjectFileOfTaskNode"; } 
    } 

} 
+3

Должен просто использовать насмешливую структуру. В NSubstitute это будет просто: var engine = Substitute.For (); – 2011-08-01 13:18:28

1

У меня была такая же проблема.Я решил это, запустив механизм сборки. Как и (AppSettings является MsBuild Task Name):

using Microsoft.Build.Framework; 
using NUnit.Framework; 
using Rhino.Mocks; 

namespace NameSpace 
{ 
    [TestFixture] 
    public class Tests 
    { 
     [Test] 
     public void Test() 
     { 
      MockRepository mock = new MockRepository(); 
      IBuildEngine engine = mock.Stub<IBuildEngine>(); 

      var appSettings = new AppSettings(); 
      appSettings.BuildEngine = engine; 
      appSettings.Execute(); 
     } 
    } 
} 
0

При сборке System.Web в namespaceSystem.Web.Compilation класс MockEngine который реализует IBuildEngineinterface способом, который описывает Тим ​​Мерфи.

Смежные вопросы