Подтвердить что ты не робот

Unit test Пользовательская задача MSBuild без попытки "Завершить попытку регистрации до ее инициализации"

Я написал несколько пользовательских задач MSBuild, которые хорошо работают и используются в процессе сборки CruiseControl.NET.

Я изменяю его и желаю unit test его, вызвав метод Task Execute().

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

Log.LogMessage("some message here");

он выдает исключение InvalidOperationException:

Задача попыталась выполнить запись до ее инициализации. Сообщение было...

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

4b9b3361

Ответ 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 таким образом, который описывает Тима Мерфи.