没有“任务在初始化之前尝试记录”的单元测试 MSBuild 自定义任务 错误

发布于 2024-07-09 00:53:40 字数 343 浏览 5 评论 0原文

我编写了一些运行良好的 MSBuild 自定义任务,并在我们的 CruiseControl.NET 构建过程中使用。

我正在修改一个,并希望通过调用任务的 Execute() 方法对其进行单元测试。

但是,如果遇到包含

Log.LogMessage("some message here");

它的行,则会抛出 InvalidOperationException:

Task 在初始化之前尝试记录。 消息是...

有什么建议吗? (过去,我主要在自定义任务上对内部静态方法进行单元测试,以避免此类问题。)

I have written a few MSBuild custom tasks that work well and are use in our CruiseControl.NET build process.

I am modifying one, and wish to unit test it by calling the Task's Execute() method.

However, if it encounters a line containing

Log.LogMessage("some message here");

it throws an InvalidOperationException:

Task attempted to log before it was initialized. Message was...

Any suggestions? (In the past I have mostly unit-tested Internal static methods on my custom tasks to avoid such problems.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

歌入人心 2024-07-16 00:53:40

您需要设置您正在调用的自定义任务的 .BuildEngine 属性。

您可以将其设置为当前任务正在使用的同一 BuildEngine,以无缝包含输出。

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

You need to set the .BuildEngine property of the custom task you are calling.

You can set it to the same BuildEngine your current task is using to include the output seamlessly.

Task myCustomTask = new CustomTask();
myCustomTask.BuildEngine = this.BuildEngine;
myCustomTask.Execute();
寒江雪… 2024-07-16 00:53:40

我发现除非任务在 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);
    }
}

I have found that the log instance does not work unless the task is running inside msbuild, so I usually wrap my calls to Log, then check the value of BuildEngine to determin if I am running inside msbuild. As below.

Tim

private void LogFormat(string message, params object[] args)
{
    if (this.BuildEngine != null)
    {
        this.Log.LogMessage(message, args);
    }
    else
    {
        Console.WriteLine(message, args);
    }
}
眼前雾蒙蒙 2024-07-16 00:53:40

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

}

@Kiff comment on mock/stub IBuildEngine is a good idea. Here is my FakeBuildEngine. C# and VB.NET examples provided.

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

}
倾其所爱 2024-07-16 00:53:40

如果您已经实现了 ITask 接口,则必须自己初始化 Log 类。

否则你应该从 Microsoft.Build.Utilities.dll 中的 Task 继承
它实现了ITask,并为您完成了大量的跑腿工作。

这是构建自定义任务的参考页面,它解释了很多内容。

构建自定义 MSBuild 任务参考

另外值得一看的是

如何调试自定义 MSBuild 任务

除此之外,您可以发布 MSBuild您用于调用自定义任务的 XML。
代码本身显然是最有帮助的:-)

If you have implemented the interface ITask you will have to initialise the Log class yourself.

Otherwise you should just inherit from Task in Microsoft.Build.Utilities.dll
That implements ITask and does a lot of the leg work for you.

Here is the reference page for building a custom task, it explains quite a lot of it.

Building a custom MSBuild task reference

Also worth a look is

How to debug a custom MSBuild task

Other then that could you post the MSBuild XML you are using for calling your custom task.
The code itself would obviously be the most help :-)

挥剑断情 2024-07-16 00:53:40

我有同样的问题。 我通过存根构建引擎解决了这个问题。 像这样(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();
        }
    }
}

I had the same problem. I solved it by stubbing the build engine. Like so (AppSettings is the MsBuild Task Name):

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();
        }
    }
}
国粹 2024-07-16 00:53:40

namespace 中的程序集 System.Web 中,System.Web.Compilation 是一个实现 IBuildEngine 的类 MockEngine 界面以一种描述 Tim Murphy 的方式。

In assembly System.Web in namespace System.Web.Compilation is a class MockEngine which implements IBuildEngine interface in a way which describes Tim Murphy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文