Unit test Task personalizzato MSBuild senza & # 8220; Task ha tentato di accedere prima che fosse inizializzato & # 8221; errore

StackOverflow https://stackoverflow.com/questions/260847

Domanda

Ho scritto alcune attività personalizzate di MSBuild che funzionano bene e sono utilizzate nel nostro processo di compilazione CruiseControl.NET.

Ne sto modificando uno e desidero testarlo unitamente chiamando il metodo Execute () di Task.

Tuttavia, se incontra una riga contenente

Log.LogMessage("some message here");

genera un InvalidOperationException:

L'attività ha tentato di accedere prima che fosse inizializzata. Il messaggio era ...

Qualche suggerimento? (In passato ho principalmente testato unitamente metodi statici interni sui miei compiti personalizzati per evitare tali problemi.)

È stato utile?

Soluzione

Devi impostare la proprietà .BuildEngine dell'attività personalizzata che stai chiamando.

È possibile impostarlo sullo stesso BuildEngine utilizzato dall'attività corrente per includere l'output senza problemi.

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

Altri suggerimenti

Ho scoperto che l'istanza di log non funziona a meno che l'attività non sia in esecuzione all'interno di msbuild, quindi di solito eseguo il wrapping delle mie chiamate a Log, quindi controllo il valore di BuildEngine per determinare se sto eseguendo all'interno di msbuild. Come sotto.

Tim

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

Il commento di @Kiff su mock / stub IBuildEngine è una buona idea. Ecco il mio FakeBuildEngine. Esempi di C # e VB.NET forniti.

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

}

Se hai implementato l'interfaccia ITask dovrai inizializzare tu stesso la classe Log.

Altrimenti dovresti ereditare da Attività in Microsoft.Build.Utilities.dll Questo implementa ITask e fa molto lavoro per te.

Ecco la pagina di riferimento per la creazione di un'attività personalizzata, ne spiega parecchio.

Creazione di un riferimento all'attività di MSBuild personalizzato

Vale anche la pena dare un'occhiata

Come eseguire il debug di un'attività MSBuild personalizzata

A parte ciò, è possibile pubblicare l'XML di MSBuild che si sta utilizzando per chiamare l'attività personalizzata. Il codice stesso sarebbe ovviamente di grande aiuto :-)

Ho avuto lo stesso problema. L'ho risolto rubando il motore di costruzione. In questo modo (AppSettings è il nome dell'attività 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();
        }
    }
}

Nell'assembly System.Web nello spazio dei nomi System.Web.Compilation è una classe MockEngine che implementa < code> IBuildEngine interfaccia in un modo che descrive Tim Murphy.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top