28

うまく機能し、CruiseControl.NET ビルド プロセスで使用される MSBuild カスタム タスクをいくつか作成しました。

私は 1 つを変更しており、タスクの Execute() メソッドを呼び出して単体テストを行いたいと考えています。

ただし、次を含む行に遭遇した場合

Log.LogMessage("some message here");

InvalidOperationException をスローします。

タスクは、初期化される前にログに記録しようとしました。メッセージは...

助言がありますか?(過去に、このような問題を回避するために、カスタム タスクで内部静的メソッドのほとんどを単体テストしてきました。)

4

6 に答える 6

28

呼び出すカスタム タスクの .BuildEngine プロパティを設定する必要があります。

出力をシームレスに含めるために、現在のタスクが使用しているのと同じ BuildEngine に設定できます。

Task myCustomTask = new CustomTask();
myCustomTask.BuildEngine = this.BuildEngine;
myCustomTask.Execute();
于 2008-11-20T14:12:07.433 に答える
15

タスクが msbuild 内で実行されていない限り、ログ インスタンスが機能しないことがわかったので、通常は呼び出しをログにラップし、次に BuildEngine の値をチェックして、msbuild 内で実行しているかどうかを判断します。以下のように。

ティム

private void LogFormat(string message, params object[] args)
{
    if (this.BuildEngine != null)
    {
        this.Log.LogMessage(message, args);
    }
    else
    {
        Console.WriteLine(message, args);
    }
}
于 2008-11-13T17:57:37.637 に答える
10

モック/スタブ IBuildEngine に関する @Kiff コメントは良い考えです。これが私の 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"; }
    }

}
于 2011-04-10T10:09:16.727 に答える
7

インターフェイス ITask を実装している場合は、自分で Log クラスを初期化する必要があります。

それ以外の場合は、Microsoft.Build.Utilities.dllのTaskから継承する必要があります。これはITaskを実装し、多くの処理を行います。

これは、カスタム タスクを作成するためのリファレンス ページです。かなり多くのことが説明されています。

カスタム MSBuild タスク リファレンスの構築

また一見の価値があるのは

カスタム MSBuild タスクをデバッグする方法

それ以外の場合は、カスタム タスクの呼び出しに使用している MSBuild XML を投稿できます。コード自体が明らかに最も役立つでしょう:-)

于 2008-11-08T12:12:28.637 に答える
3

私も同じ問題を抱えていました。ビルドエンジンをスタブすることで解決しました。そのように (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();
        }
    }
}
于 2012-03-04T22:14:31.953 に答える