2013-03-19 256 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