2008-11-04 12 views
25

Tôi đã viết một vài tác vụ tùy chỉnh MSBuild hoạt động tốt và được sử dụng trong quy trình xây dựng CruiseControl.NET của chúng tôi.Kiểm tra đơn vị MSBuild Custom Task mà không có "Tác vụ cố gắng đăng nhập trước khi nó được khởi tạo" lỗi

Tôi đang sửa đổi một đơn vị và muốn đơn vị kiểm tra nó bằng cách gọi phương thức Thực hiện của tác vụ().

Tuy nhiên, nếu nó gặp một dòng chứa

Log.LogMessage("some message here"); 

nó ném một InvalidOperationException:

công tác cố gắng đăng nhập trước khi nó được khởi tạo. Thư là ...

Mọi đề xuất? (Trong quá khứ, tôi có hầu hết các phương pháp tĩnh nội bộ được thử nghiệm trên các nhiệm vụ tùy chỉnh của tôi để tránh các vấn đề này.)

+1

Tôi vừa mới gặp phải điều này trên một số tác vụ tùy chỉnh cho câu trả lời của SO - Branstar là chính xác! Chỉ cần thiết lập BuildEngine trên nhiệm vụ được gọi. –

Trả lời

24

Bạn cần phải thiết lập thuộc tính của nhiệm vụ tùy chỉnh bạn đang gọi .BuildEngine.

Bạn có thể đặt nó vào cùng một BuildEngine tác vụ hiện tại của bạn đang sử dụng để bao gồm đầu ra liền mạch.

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

Đây chính xác là những gì tôi cần - cảm ơn! –

+12

Vì câu hỏi nằm trong ngữ cảnh của một bài kiểm tra đơn vị, tôi cũng sẽ bổ sung thêm rằng bạn có thể thiết lập thuộc tính BuildEngine thành đối tượng giả/sơ đồ thực hiện giao diện IBuildEngine. –

7

Nếu bạn đã triển khai giao diện ITask, bạn sẽ phải tự khởi tạo lớp Đăng nhập.

Nếu không, bạn chỉ cần kế thừa từ công tác trong Microsoft.Build.Utilities.dll Đó thực hiện ITask và hiện rất nhiều công việc chân cho bạn.

Đây là trang tham khảo để xây dựng một tác vụ tùy chỉnh, nó giải thích khá nhiều về nó.

Building a custom MSBuild task reference

Cũng đáng xem là

How to debug a custom MSBuild task

khác sau đó bạn có thể gửi XML MSBuild bạn đang sử dụng để gọi nhiệm vụ tùy chỉnh của bạn. Mã sẽ rõ ràng là trợ giúp nhiều nhất :-)

+0

+1 ... liên kết tốt đẹp – alexandrul

14

Tôi thấy rằng bản ghi nhật ký không hoạt động trừ khi tác vụ đang chạy bên trong msbuild, vì vậy tôi thường kết nối các cuộc gọi của mình với Nhật ký, sau đó kiểm tra giá trị BuildEngine để xác định nếu tôi đang chạy bên trong msbuild. Như sau.

Tim

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

@Kiff nhận xét về mock/stub IBuildEngine là một ý tưởng hay. Đây là FakeBuildEngine của tôi. Ví dụ C# và VB.NET được cung cấp.

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

Chỉ nên sử dụng khung mocking. Trong NSubstitute nó sẽ chỉ là: var engine = Substitute.For (); –

+2

Phải, nếu bạn chỉ muốn tránh 'InvalidOperationException', thì không cần phải thực hiện cụ thể. Miễn là cá thể không phải là null, bạn sẽ không thấy 'InvalidOperationException'. Đối với Moq, nó sẽ là 'myCustomTask.BuildEngine = new Mock (). Đối tượng;'. Ít mã hơn cho lỗi ít hơn :) – mikegradek

+0

@perropicante Đúng Moq có thể đã được sử dụng. Vào thời điểm đó tôi đã có nhiều bài kiểm tra cần có IBuildEngine và việc triển khai cụ thể dễ sử dụng hơn và làm cho các bài kiểm tra dễ đọc hơn. –

1

tôi đã cùng một vấn đề.Tôi đã giải quyết nó bằng cách đặt công cụ xây dựng. Như vậy (AppSettings là MSBuild nhiệm 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(); 
     } 
    } 
} 
0

Trong lắp ráp System.Web trong namespaceSystem.Web.Compilation là một lớp MockEngine mà thực hiện IBuildEngineinterface theo cách mô tả Tim Murphy.

+0

... trong ngôi nhà mà Jack xây dựng. –