Unit test Task personalizzato MSBuild senza & # 8220; Task ha tentato di accedere prima che fosse inizializzato & # 8221; errore
-
06-07-2019 - |
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.)
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.