2017-04-11 101 views
2

我必須使用一些我無法更改的遺留代碼。我必須編寫實現了一個名爲「ITask」的接口的類,該接口具有一個名爲「RunTask」的方法,該方法接受稱爲Schedule的具體類型例如;我可以模擬SqlConnection.BeginTransaction c#?

public void RunTask(Schedule thisSchedule) 
{ 
    //I have to do stuff with thisSchedule, no I can't fix that name... 
} 

雖然舊的代碼不使用單元測試我倒是十分喜歡用它爲我的工作,但問題是「thisSchedule」。我製作了一個僞造的Schedule版本,它試圖控制它內部的所有方法的功能(這可能是一個傻瓜的差事)。到目前爲止,我已經成功地通過利用虛驚的數量虛擬方法或通過使用反射,但我已經打到我的第一個顯示停止

我無法連接到真正的數據庫,我有一個經常調用的方法;

public void BeginTransaction() 
{ 
    MyTransaction = MyConnection.BeginTransaction(IsolationLevel.RepeatableRead); 
} 

internal SqlConnection MyConnection = new System.Data.SqlClient.SqlConnection(); 

這將引發異常,因爲連接被關閉,我非常希望能夠設置一個標誌,說明該方法被調用,然後就吞下例外,但我會很樂意簡單地忽略異常。

任何事情,無論多麼令人討厭,這將讓我通過這種方法調用將是一個可以接受的答案。這是或失敗。

我不允許在Visual Studio企業中使用typeMock或Shim等付費服務。

編輯

using System; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Data; 
using System.Data.SqlClient; 

namespace PredictionServicesTests 
{ 
    [TestClass] 
    public class UnitTest1 
    { 
     [TestMethod] 
     public void BeginTransaction_WhenCalled_SetsTransactionStartedToTrue() 
     { 
      Schedule schedule = new FakeSchedule(); 

      schedule.BeginTransaction(); //It would be enough to simply get past this line to the Assert 

      Assert.IsTrue(((FakeSchedule)schedule).TransactionStarted); //This would be nice, but isn't vital 
     } 

     class Schedule 
     { 
      private SqlTransaction MyTransaction; 
      internal SqlConnection MyConnection = new System.Data.SqlClient.SqlConnection(); 
      public void BeginTransaction() 
      { 
       MyTransaction = MyConnection.BeginTransaction(IsolationLevel.RepeatableRead); 
      } 
     } 

     class FakeSchedule : Schedule 
     { 
      public bool TransactionStarted { get; set; } 
     } 
    } 
} 

請記住,我不能改變的時間表類,只要事情能這麼簡單!

ITask接口;

interface ITask 
    { 
     void RunTask(Schedule thisSchedule); 
    } 
+0

您可以勻/僞造它與微軟的正版正貨取決於您的Visual Studio版本。在2015/2017年,它包含在企業版中。在2013年,它包含在Premium和Ultimate版本中。 – Igor

+3

你可以使用免費的Moq。也看看使用接口。將使單元測試這種類型的東西更容易。在交易的情況下,也許你可以通過IDbTransaction。 –

+0

「不允許使用typeMock或Shims之類的付費服務」這是一個技術限制嗎? – mxmissile

回答

0

我能得到的最接近的是創建一個名爲IFake一個界面,我將適用於FakeSchedule;

public interface IFake 
{ 
    Action GetFakeAction(Action action); 
} 

public class FakeSchedule : Schedule, IFake 
{ 
    public bool TransactionStarted { get; set; } 

    public Action GetFakeAction(Action action) 
    { 
     return() => TransactionStarted = true; 
    } 
} 

然後我創建了下面的擴展方法;

public static void Testable<T>(this T obj, Action action) 
    { 
     if(obj is IFake) 
     { 
      action = ((IFake)obj).GetFakeAction(action); 
     } 
     action(); 
    } 

這讓我可以像這樣調用BeginTransaction方法;

[TestMethod] 
    public void BeginTransaction_WhenCalled_SetsTransactionStartedToTrue() 
    { 
     Schedule schedule = new FakeSchedule(); 
     var connection = new SqlConnection(); 

     schedule.Testable(schedule.BeginTransaction); 

     Assert.IsTrue(((FakeSchedule)schedule).TransactionStarted); 
    } 

顯然,這個解決方案很不理想,但它的工作原理,如果我運行一個單元測試,然後調用開始交易轉向而真正的實現了。我執行的版本將需要一些工作(我的GetFakeAction的實現太愚蠢),但它是最接近我想我會得到的,唯一的其他解決方案是使用Prig或遵循NKosi's answer並連接到一個虛擬數據庫。

1

事實上,您正在「新建」類中的SQL連接,使得很難反轉控件並使其更易於進行單元測試。

另一種選擇是連接到一個虛擬數據庫並使用該連接。虛擬數據庫將足以允許測試作爲集成測試來執行。

遺留代碼應視爲您無法控制的第三方代碼。而且你不應該浪費時間測試你無法控制的第三方代碼。通常的做法是將第三方代碼封裝在您控制和使用的抽象背後。遺留代碼證明technical debt正在兌現目前由於設計不佳而出現的困難。

考慮到目前的限制,沒有其他的事情可以做。

1

當談到單元測試時,需要隔離任何外部依賴。不同的框架有不同的隔離外部依賴的方式,以及它們可以隔離的限制。

許多框架允許您創建一個接口的模擬,以便您可以爲處於受控狀態的成員提供實現。很多這些框架也適用於抽象類的抽象成員。然而,很少的框架支持在具體類(非抽象成員)上提供實現的能力。僅有的兩個我知道哪些是可用的:


這兩個框架利用.NET API探查攔截成員呼叫來取代它們您提供的代碼。我對TypeMock Isolator不太熟悉,但我對Microsoft Fakes非常熟悉。 Microsoft Fakes框架支持生成一個程序集(即System.Fakes.dll),該程序集包含允許您在成員上提供自己的實現的類。它支持針對接口和抽象類(相當於「嘲笑」)創建存根,以及針對具體和靜態類創建Shim。

如果您選擇使用微軟正版正貨,你首先需要生成對System.Data.dll中假的組裝,這就是SqlConnection所在。這將生成ShimSqlConnection類,這個類包含的成員,讓你可以提供SqlConnection類的成員替代實現。下面是如何可以做一個例子:

[TestMethod] 
public void SampleTest() 
{ 
    using (ShimsContext.Create()) 
    { 
     // Arrange 
     SqlConnection.AllInstances.BeginTransactionIsolationLevel = 
      (instance, iso) => { ... }; 

     // Act 

     // Assert 
    } 
} 

當然,無論是模擬,墊片,存根,無論...你應該提供替代實現它模擬你想要的行爲。由於您要更換SqlConnection.BeginTransaction(IsolationLevel)會員,因此您必須遵守合同和預期行爲;你不應該返回一個值或拋出一個實際實現不會執行的異常。在我的公司,我們利用微軟正版正貨來模擬場景,如:

  • 如果當我們從它讀取Stream被關閉,會發生什麼?
  • 如果用戶名/密碼是錯誤的一個HttpWebRequest會發生什麼?
  • 如果我們希望我們的數據庫返回兩行(即控制SqlDataReader.Read()成員)會發生什麼?

在所有這些場景中,我們的目標是隔離這些成員的已實施行爲並將其替換爲實際可能發生在受控實驗中的實現。


更新:

我只注意到樓主說「我不允許使用付費樣typeMock或Visual Studio企業服務墊片。」。如果這是一個限制,那麼你將不得不尋找另一個我不知道使用.NET Profiler API或自己利用框架的工具。 Profiling (Unmanaged API Reference)

+0

正如您在更新中正確指出的那樣,我無法訪問付費服務,因爲這些信息是有用的,但它不回答原始問題,但是[prig ](https://marketplace.visualstudio.com/items?itemName=sug.Prig-OpenSourceAlternativetoMicrosoftFakes)已被建議作爲免費的替代品,我還沒有嘗試 –

+0

@mark_h:有趣的。我從來沒有聽說過prig,但很高興知道第三個框架存在,並且這個框架完全是免費/開源的。 –

相關問題