2017-07-25 78 views
1

我有一個方法從另一個類中調用另一個方法,該方法又在第二個類中執行許多私有方法。一種方法是寫入一個文件,然後由外部進程拾取文件,外部進程根據需要處理的內容運行5到30分鐘,然後生成另一個文件,然後由我的應用程序讀取並讀取數據並將數據返回到被調用的初始方法。單元測試 - 如何測試長時間運行的外部進程

我知道我所描述的不是一個「單元」,但方法是公開的,所以我的問題是我需要在這個方法中測試什麼,以及我怎樣才能在第二類中嘲弄調用方法?或者我只是讓方法正常運行,無論是5分鐘還是30分鐘?

class A 
{ 
    public List<DataClass> MethodUnderTest() 
    { 
     List<string> requiredData; 
     SecondClass B = New SecondClass(); 
     requiredData = B.GenerateFile(); 
     //B.GenerateFile() Executes a number of private methods within the SecondClass, 
     //This can be treated as a service call. This runs between 5 and 30 mins 
     return requiredData.Select(r => new DataClass{ 
            Property1 = r.Substring(0,2), 
            Property2 = r.Substring(3,5), 
            Property3 = r.Substring(9,10) 
            }).ToList(); 
    } 
} 
+0

發生在這裏的太多顧慮。你最終想要測試什麼?如果涉及的類是我們設計的,那麼您應該用單元測試替代依賴關係。根據描述這種緊密耦合的氣味。 – Nkosi

+0

我不是SecondClass的原始開發人員,也不是我編碼的方式,但它是我所堅持的。我最終想測試B.GenerateFile返回列表,然後我可以解析成列表 gheff

+1

如果您實際上並未嘗試單元測試SecodClass,那麼將SecondClass作爲第三方並將其抽象爲您控制的代碼的後面。這樣你就可以隔離你的代碼並正確地維護/測試它。 – Nkosi

回答

1

款待SecondClass作爲第三方的依賴,將其封裝在你背後控制代碼創建的,你從依賴想要的功能抽象;

public interface ISecondClass { 
    List<string> GenerateFile(); 
} 

A類也有太多的顧慮,應該刪除任何不屬於A級的責任/問題。

public interface IDataClassParser { 
    DataClass Parse(string data); 
} 

public class DefaultDataClassParser : IDataClassParser { 
    public DataClass Parse(string data) { 
     return new DataClass { 
      Property1 = data.Substring(0, 2), 
      Property2 = data.Substring(3, 5), 
      Property3 = data.Substring(9, 10) 
     }; 
    } 
} 

以上是用於演示目的的簡單示例。

重構目標類到現在明確依賴於抽象而不是結核。

public class A { 
    private readonly ISecondClass B; 
    private readonly IDataClassParser parser; 

    public A(ISecondClass B, IDataClassParser parser) { 
     this.B = B; 
     this.parser = parser; 
    } 

    public List<DataClass> MethodUnderTest() { 
     List<string> requiredData = B.GenerateFile(); 
     return requiredData.Select(createNewDataClass).ToList(); 
    } 

    private DataClass createNewDataClass(string r) { 
     return parser.Parse(r); 
    } 
} 

A不再緊密耦合到實施關注和現在被測方法可以單獨測試。

實施例測試

[TestClass] 
public class ATest { 
    [TestMethod] 
    public void MethodUnderTest_Should_Return_DataClassList() { 
     //Arrange 
     List<string> mockData = new List<string>(); 
     //TODO: Populate mockData 
     var mockB = new Mock<ISecondClass>(); 
     mockB.Setup(_ => _.GenerateFile()).Returns(mockData); 

     var sut = new A(mockB.Object, new DefaultDataClassParser()); 

     //Act 
     var actual = sut.MethodUnderTest(); 

     //Assert 
     //TODO: assert that the actual result satisfies expectations 
    }   
} 

技術上上述現在只實際測試分析器,因此額外的測試可被寫入,以測試在隔離解析代碼。

[TestClass] 
public class DataClassParserTest { 
    [TestMethod] 
    public void DataClassParser_Should_Return_DataClass() { 
     //Arrange 
     string mockData = "..."; //TODO: Populate mockData 
     var sut = new DefaultDataClassParser(); 

     //Act 
     var actual = sut.Parse(mockData); 

     //Assert 
     //TODO: assert that the actual result satisfies expectations 
    }   
} 

最後,在生產,你的長期運行的類的實現將從抽象推導和封裝相關SecondClass

public class SecondClassWrapper : ISecondClass { 
    private SecondClass B = new SecondClass(); 
    public List<string> GenerateFile() { 
     return B.GenerateFile(); 
    } 
} 
1

我覺得你有種回答了你自己的問題,但我將概述我會做什麼:創建二等一個接口,並取決於(你將需要注入的依賴(你喜歡的任何方式)) ,那麼我會在單元測試中嘲笑它的行爲。那個moq會做這個工作。這給我們提供了測試的邏輯:好吧,唯一有趣的細節是子串的東西 - 所以測試。然後你需要訪問SecondClass,看看你可以在那裏創建什麼樣的單元測試。

單位和非單位測試怎麼樣?我說你需要這兩者,但你會執行它們不同(每當你改變你的機器上的代碼時應該運行單元(所以,他們需要快速執行(因此模擬是關鍵),如幾毫微秒),以及將執行所有提交依賴項(通常在構建服務器上)的集成/功能測試,而且它們需要更多時間(在您的情況下至少需要30s到幾m),但這很好,因爲它們並不經常運行。

1

你試圖確定如何進行單元測試的方法類(A)調用另一個類的方法(SecondClass)。你正在測試的方法並沒有太大的作用。大多數情況下,如果它調用的方法工作,它將工作。所以(GenerateFile)是專注於編寫測試的最佳地點。

您提到SecondClass調用了一些私有方法。這些方法有多複雜?我猜測(我可能會走),其中一些比較複雜,因爲調用它們的方法需要很長時間才能運行。如果構成長時間運行流程的小任務經過測試,那麼您的流程就是經過測試的方法或類的組合。

如果這些私有方法很複雜,那麼您無法通過測試調用調用這些方法的方法的方法來測試它們。也許這些私有方法的一些行爲可以放在單獨的類中,這些類可以單獨進行測試。

聽起來好像有很多移動的東西,但爲了獲得測試的好處,我們必須編寫可以測試的代碼。這改變了我們編寫代碼的方式,並且通常會更好。

讓我們回到最初被問及方法:
您有需要List<string>並將其轉換爲一個List<DataClass>的方法。爲了簡單起見,爲什麼不寫一個方法將string轉換爲DataClass?也許你可以把它放在一個擴展或甚至在自己的班級。

DataClass FromString(string input) 
{ 
    return new DataClass{ 
     Property1 = r.Substring(0,2), 
     Property2 = r.Substring(3,5), 
     Property3 = r.Substring(9,10) 
    } 
} 

然後你就可以測試方法,以確保從字符串解析的DataClass具有所期望的屬性值。而你之前的陳述變得如此簡單,以至於它甚至不需要進行自己的測試。

return requiredData.Select(r => FromString(r)).ToList(); 

正如在另一個答案建議,SecondClass最好應在其A依賴的接口。但即使它不是,如果SecondClass本身就是單元測試,那麼MethodUnderTest將會是合理的,因爲它除了調用一個測試方法並將結果傳遞給另一個測試方法外什麼都不做。

但是,如果你用一個接口代替SecondClass然後MethodUnderTest變得超級簡單的測試,因爲你只需使用「測試雙」,一個簡單的實現僅用於測試目的是很難編碼爲返回,你要測試的值。所以你的測試有效地說:「假設ISecondClass返回這些值,我期望MethodUnderTest返回這些值。」它不需要幾分鐘即可運行,只需幾毫秒。

即使所有部分都經過了測試,您仍然希望進行集成測試,從頭到尾運行整個測試。你可以創建一個數據較少的小文件,並針對這個測試運行測試嗎?

相關問題