Unit test Пользовательская задача MSBuild без попытки "Завершить попытку регистрации до ее инициализации"
Я написал несколько пользовательских задач MSBuild, которые хорошо работают и используются в процессе сборки CruiseControl.NET.
Я изменяю его и желаю unit test его, вызвав метод Task Execute().
Однако, если он встречает строку, содержащую
Log.LogMessage("some message here");
он выдает исключение InvalidOperationException:
Задача попыталась выполнить запись до ее инициализации. Сообщение было...
Любые предложения? (В прошлом у меня в основном были проверены на предмет встроенных статических методов для моих пользовательских задач, чтобы избежать таких проблем.)
Ответы
Ответ 1
Вам нужно установить свойство .BuildEngine для настраиваемой задачи, которую вы вызываете.
Вы можете установить его на тот же BuildEngine, который использует ваша текущая задача, чтобы легко включать вывод.
Task myCustomTask = new CustomTask();
myCustomTask.BuildEngine = this.BuildEngine;
myCustomTask.Execute();
Ответ 2
Я обнаружил, что экземпляр журнала не работает, если задача не запущена внутри msbuild, поэтому я обычно переношу свои вызовы в журнал, а затем проверяю значение BuildEngine на определение, если я запускаю внутри msbuild. Как показано ниже.
Тим
private void LogFormat(string message, params object[] args)
{
if (this.BuildEngine != null)
{
this.Log.LogMessage(message, args);
}
else
{
Console.WriteLine(message, args);
}
}
Ответ 3
Если вы внедрили интерфейс ITask, вам придется самостоятельно инициализировать класс журнала.
В противном случае вы должны просто наследовать от Задача в Microsoft.Build.Utilities.dll
Это реализует ITask и делает для вас много работы.
Вот справочная страница для создания пользовательской задачи, она объясняет довольно много.
Создание настраиваемой справки задания MSBuild
Также стоит посмотреть
Отладка пользовательской задачи MSBuild
Другое, что вы могли бы опубликовать XML файл MSBuild, который вы используете для вызова своей настраиваемой задачи.
Очевидно, что сам код будет наиболее полезен: -)
Ответ 4
Комментарий @Kiff для mock/stub IBuildEngine - хорошая идея. Вот мой FakeBuildEngine. Приведены примеры С# и VB.NET.
VB.NET
Imports System
Imports System.Collections.Generic
Imports Microsoft.Build.Framework
Public Class FakeBuildEngine
Implements IBuildEngine
// It 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
С#
using System;
using System.Collections.Generic;
using Microsoft.Build.Framework;
public class FakeBuildEngine : IBuildEngine
{
// It 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"; }
}
}
Ответ 5
У меня была та же проблема. Я решил это, запустив механизм сборки. Так же (AppSettings - это имя задачи MsBuild):
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();
}
}
}
Ответ 6
В сборке System.Web
в namespace
System.Web.Compilation
представлен класс MockEngine
, который реализует IBuildEngine
interface
таким образом, который описывает Тима Мерфи.