我编写了一些MSBuild自定义任务,这些任务运行良好,并在我们的CruiseControl.NET构建过程中使用。
我正在修改一个,并希望通过调用Task的Execute()方法对其进行单元测试。
但是,如果遇到包含
的行Log.LogMessage("some message here");
抛出InvalidOperationException:
任务尝试在初始化之前进行记录。消息是......
有什么建议吗? (过去我在自定义任务上主要使用单元测试内部静态方法来避免此类问题。)
答案 0 :(得分:26)
您需要设置要调用的自定义任务的.BuildEngine属性。
您可以将其设置为当前任务使用的相同BuildEngine,以无缝地包含输出。
Task myCustomTask = new CustomTask();
myCustomTask.BuildEngine = this.BuildEngine;
myCustomTask.Execute();
答案 1 :(得分:14)
我发现除非任务在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);
}
}
答案 2 :(得分:8)
@Kiff评论模拟/存根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"; }
}
}
答案 3 :(得分:7)
如果您已经实现了接口ITask,则必须自己初始化Log类。
否则,您应该继承Microsoft.Build.Utilities.dll中的任务 这实现了 ITask ,为你做了很多工作。
以下是构建自定义任务的参考页面,它解释了很多。
Building a custom MSBuild task reference
另外值得一看的是
How to debug a custom MSBuild task
除此之外,您可以发布用于调用自定义任务的MSBuild XML。 代码本身显然是最有帮助的: - )
答案 4 :(得分:1)
我遇到了同样的问题。我通过存根构建引擎解决了这个问题。像这样(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();
}
}
}
答案 5 :(得分:0)
在System.Web
namespace
中的汇编System.Web.Compilation
中有MockEngine
个类,它以描述Tim Murphy的方式实现IBuildEngine
interface
。