Модульный тест MSBuild Custom Task без ошибки «Задача попыталась войти в систему до ее инициализации» - PullRequest
25 голосов
/ 04 ноября 2008

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

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

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

Log.LogMessage("some message here");

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

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

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

Ответы [ 6 ]

26 голосов
/ 20 ноября 2008

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

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

Task myCustomTask = new CustomTask();
myCustomTask.BuildEngine = this.BuildEngine;
myCustomTask.Execute();
14 голосов
/ 13 ноября 2008

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

Тим

private void LogFormat(string message, params object[] args)
{
    if (this.BuildEngine != null)
    {
        this.Log.LogMessage(message, args);
    }
    else
    {
        Console.WriteLine(message, args);
    }
}
8 голосов
/ 10 апреля 2011

@ Кифф комментирует макет / заглушку. 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"; }
    }

}
7 голосов
/ 08 ноября 2008

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

В противном случае вы должны просто наследовать от Задача в Microsoft.Build.Utilities.dll Это реализует ITask и делает большую часть работы за вас.

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

Создание пользовательской ссылки на задачу MSBuild

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

Как отладить пользовательскую задачу MSBuild

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

1 голос
/ 05 марта 2012

У меня была такая же проблема. Я решил это, заглушив сборку двигателя. Вот так (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();
        }
    }
}
0 голосов
/ 01 сентября 2016

В сборке System.Web в namespace System.Web.Compilation - это класс MockEngine, который реализует IBuildEngine interface способом, который описывает Тима Мерфи.

...