2013-03-19 240 views
3

我試圖寫ServiceStack服務簡單的單元測試,我會通過他們在網上已經測試和幾個線程在這裏。這是我想要完成的大部分細節的主線 - Unit Test HTTPRequest Headers with ServiceStack單元測試

不過,我對着注射的IDbConnection對象到服務的問題。在webmethod中,使用OrmLite的GetDictionary方法填充字典對象。但我無法嘲笑它,因爲GetDictionary是擴展方法。

private Mock<IDbConnection> _dbConnectionMock; 
private Dictionary<string, string> _nameValuePairs; 

[SetUp] 
public void SetUp() 
{ 
    _dbConnectionMock = new Mock<IDbConnection>(); 

    _nameValuePairs = new Dictionary<string, string>() 
    { 
      {"name","test"}, 
      {"Updatedby", "5/23/12 7:00:15 AM"}, 
      {"Address", "212 Adam St."} 
    }; 
} 

在測試方法

var service = new CustomerLookupService(_dbConnectionMock.Object); 
var response = (HttpResult)service.Any(new CustomerLookup { name = "test" }); 
//assert statements 

如果GetDictionary方法不能嘲笑,我甚至願意叫擊中DB Web方法,這個我需要創建APPHOST。

回答

3

我認爲有幾個選項來考慮。

  • 嘲諷/存根/單元測試擴展方法herehere或各種其他的斑點。我不認爲有這樣做的首選方法,但有一些選項和框架/庫來幫助。

  • 在單元測試中運行內存數據庫(如Sqlite)。請參閱here

  • 你可以抽象IDConnection成CustomerLookUpRepository並注入您的CustomerLookUpRepository爲您服務。那麼你可以嘲笑你的'Repository'。

我已經給了這個'安排'一試。到目前爲止,它似乎適用於大多數基本案例。數據訪問模式取自Redis Web Service example。 YMMV,但。

測試(使用RhinoMocks)

public void SomeTest() 
    { 
     var _nameValuePairs = new Dictionary<string, string>() 
     { 
      {"name","test"}, 
      {"Updatedby", "5/23/12 7:00:15 AM"}, 
      {"Address", "212 Adam St."} 
     }; 

     var mockSqlRepository = MockRepository.GenerateMock<ISqlRepository>(); 
     mockSqlRepository.Stub(
      x => x.Exec(Arg<Func<IDbConnection, Dictionary<string, string>>>.Is.NotNull)).Return(_nameValuePairs); 

     var service = new CustomerLookupService { SqlRepository = mockSqlRepository } 
     //MORE TEST CODE... 
    } 

服務類 - 使用ISqlRepository抽象/隱藏的IDbConnection。 ISqlRepository具有以函數作爲參數的函數。 (參數的)函數簽名將IDbConnection作爲參數,所以我不必編寫幾個訪問數據庫的方法。

public class CustomerLookupService 
{ 
    public ISqlRepository SqlRepository { get; set; } 

    public void Any(CustomerLookup request) 
    { 
     var results = 
      SqlExec<Dictionary<string, string>>((con) => con.GetDictionary<type, type>("Select id, name from table")); 
     //MORE SERVICE CODE 
    } 

    public T SqlExec<T>(Func<IDbConnection, T> fn) 
    { 
     return SqlRepository.Exec(fn); 
    } 
} 

ISqlRepository

public interface ISqlRepository 
{ 
    T Exec<T>(Func<IDbConnection, T> fn); 
} 

SqlRepository

public class SqlRepository : ISqlRepository 
{ 
    public IDbConnectionFactory DbFactory { get; set; } 

    public T Exec<T>(Func<IDbConnection, T> fn) 
    { 
     using (var con = DbFactory.OpenDbConnection()) 
     { 
      return fn(con); 
     } 
    } 
} 
+0

它的工作原理就像一個魅力。我有一個疑問,如果我們有多個讀取操作來檢索方法中的字典,如何模擬測試中的字典。我們是否需要按照相同的順序提及模擬對象。 – Sunny 2013-04-03 04:22:15

+0

如果您正在測試將執行許多數據庫操作且每個操作返回相同Type的方法,則此解決方案可能不是最好的。也許可以用犀牛製品基於「調用順序」上實現返回不同的充值 - http://stackoverflow.com/questions/5609589/ordering-method-return-values-with-rhino-mock-stubs – paaschpa 2013-04-03 04:33:06

+0

感謝回覆。你知道多個數據庫操作的任何替代想法嗎?實際上,我喜歡這種方法,我不需要通過違反DRY原則來創建單個存儲庫。 – Sunny 2013-04-03 04:56:56