利用Stub來打破關連性 (1)

Source: The art of unit testing with examples in .NET, Roy Osherove
http://www.amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274/ref=sr_1_1?ie=UTF8&s=books&qid=1267086529&sr=1-1
Chapter 3 Using stubs to break dependencies

在撰寫測試程式時, 遇到最大的問題是, 受測程式和其他程式有關聯. 因為這樣的關連, 會導致你要進行單元測試的困難度.

例如下面這個程式, 若是你要測試時, 必需要和file system有互動. 
public bool IsValidLogFileName(string fileName) {
  // read through the configuration file
  // return true if configuration says extension is suported
}

大家可能覺得這沒什麼, 可是如果我們推到真實世界的例子, 它可能是要存到XML, 或是資料庫中. 如果這種狀況, 大家就會覺得很辛苦, 因為你可能要用到存取XML的library, 或者是存取資料庫的library. 不但測試程式會變得比較複雜, 測試執行時間也會比較久一點.

因此在做單元測試時, 一個很重要觀念便是如何打破這樣的關連性, 讓你可以擁有完全的控制權或自主權. 在這個章節中作者介紹的手法便是利用stub.

那什麼是stub呢? 作者的定義如下:
(1) A stub is a controllable replacement for an existing dependnency ( or calllaborator) in the system.
(2) 藉由使用stub, 你可以直接測試受測程式, 而不用處理這樣的相依性.

簡單的說, 就是要用stub來取代真正的東西. 可是接下來你會問, 哪個部份可以被取代呢? 怎樣被取代呢? 這就是單元測試藝術所在, 你必須找到正確的地方, 去增加或使用一個indirection的layer, 來測試你要處理的受測程式. 以下作者列出了一些手法, 來告訴我們如何增加這個layer.

原先受測程式的原始碼:
public class LogAnalyzer {

  public bool IsValidLogFileName(string fileName) {
    IExtensionManager mgr = new FileExtensionsManager();
    return mgr.IsValid(fileName);
  }
}

方法1.
// (1) 定義一個interface
public interface IExtensionManager {
  public bool IsValid(string fileName);
}

// (2) 實作這個interface, 讓它呼叫到原先要關聯的部份
class FileExtensionManager : IExtensionManager {
  // 實作真正要做的事情
  public bool IsValid(string fileName) { ... }
}

// (3) 修改原先受測程式的內容
public class LogAnalyzer {
  private IExtensionManager manager;
 
  // 設定指向真的File Extension Manager
  public LogAnalyzer() {
    manager = new FileExtensionManager();
  }
 
  // 設定指向File Extension Manager的 stub
  public LogAnalyzer(IExtensionManager mgr) {
    manager = mgr;
  }
 
  public bool IsValidLogFileName(string fileName) {
    // 呼叫在construction階段所設定的File Extension Manager
    return manager.IsValid(fileName);
  }
}

// (4) 測試程式
[TestFixture]
public class LogAnnalyzerTests {
 
  [Test]
  public void IsValidFileName_NameShorterThan6() {
   
    // 設定stub的內容
    StubExtensionManager myFakeManager =
      new StubExtensionManager();
    myFakeManager.ShouldExtensionBeValid = true;
   
    // 把stub設定到受測程式中
    LogAnalyzer log = new LogAnalyzer(myFakeManager);
   
    // 進行測試
    bool result = log.IsValidLogFileName( "short.ext" );
   
    // 檢查測試結果
    Assert.IsFalse( result, "File name with less than 5 chars should have failed the method." );
  }
 
  // File Extension Manager的stub 
  internal class StubExtensionManager : IExtensionManager {
    public bool ShouldExtensionBeValid;
   
    public bool IsValid(string fileName) {
      //實作僅供測試使用的功能
      return ShouldExtensionBeValid;
    }
  }
}

arrow
arrow
    全站熱搜

    kojenchieh 發表在 痞客邦 留言(0) 人氣()