Test unitaire de la tâche personnalisée MSBuild sans «tentative de journalisation de la tâche avant son initialisation» erreur

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

Question

J'ai écrit quelques tâches personnalisées MSBuild qui fonctionnent bien et sont utilisées dans notre processus de génération CruiseControl.NET.

J'en modifie un et je souhaite le tester en appelant la méthode Execute () de la tâche.

Cependant, s'il rencontre une ligne contenant

Log.LogMessage("some message here");

il lève une exception InvalidOperationException:

La tâche a tenté de se connecter avant son initialisation. Le message était ...

Des suggestions? (Dans le passé, j’avais pour la plupart des méthodes statiques internes internes à l’essai spécifique sur mes tâches personnalisées pour éviter de tels problèmes.)

Était-ce utile?

La solution

Vous devez définir la propriété .BuildEngine de la tâche personnalisée que vous appelez.

Vous pouvez le définir sur le même BuildEngine que votre tâche actuelle utilise pour inclure la sortie de manière transparente.

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

Autres conseils

J'ai constaté que l'instance de journal ne fonctionnait pas si la tâche ne s'exécutait pas dans msbuild. J'ai donc l'habitude d'envelopper mes appels dans Log, puis de vérifier la valeur de BuildEngine pour déterminer si j'exécute dans msbuild. Comme ci-dessous.

Tim

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

Les commentaires de @Kiff sur le mock / stub IBuildEngine est une bonne idée. Voici mon FakeBuildEngine. Exemples fournis en C # et 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"; }
    }

}

Si vous avez implémenté l'interface ITask, vous devez initialiser vous-même la classe Log.

Sinon, vous devez simplement hériter de la tâche dans Microsoft.Build.Utilities.dll. Cela met en œuvre ITask et effectue beaucoup de travail pour vous.

Voici la page de référence pour la création d'une tâche personnalisée. Elle en explique une bonne partie.

Création d'une référence de tâche MSBuild personnalisée

Il vaut également la peine de jeter un coup d’œil sur

Comment déboguer une tâche MSBuild personnalisée

Sinon, vous pourriez publier le fichier XML MSBuild que vous utilisez pour appeler votre tâche personnalisée. Le code lui-même serait évidemment le plus utile: -)

J'ai eu le même problème. Je l'ai résolu en écrasant le moteur de construction. Comme si (AppSettings est le nom de la tâche 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();
        }
    }
}

Dans l'assembly System.Web dans l'espace de noms System.Web.Compilation est une classe MockEngine qui implémente < code> IBuildEngine interface d’une manière qui décrit Tim Murphy.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top