【问题标题】:Unit tests for ServiceStack servicesServiceStack 服务的单元测试
【发布时间】:2013-03-19 20:57:38
【问题描述】:

我正在尝试为 ServiceStack 服务编写简单的单元测试,我正在通过他们在线的测试和这里的几个线程。这是我想要完成的大部分细节的主线程 - Unit Test HTTPRequest Headers with ServiceStack。

但是,我在将 IDbConnection 对象注入服务时遇到了问题。在 web 方法中,使用 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方法不能被mock,我什至愿意调用命中DB的web方法,为此我需要创建AppHost。

【问题讨论】:

    标签: unit-testing nunit moq servicestack


    【解决方案1】:

    我认为有几个选项可供研究。

    • 模拟/存根/单元测试扩展方法here、here 或其他各种地方。我认为没有首选的方法可以做到这一点,但有一些选项和框架/库可以提供帮助。

    • 为单元测试运行内存数据库(例如 Sqlite)。见here。

    • 您可以将 IDConnection 抽象为 CustomerLookUpRepository 并将您的 CustomerLookUpRepository 注入您的服务。然后你可以模拟你的“存储库”。

    我已经尝试过这种“安排”。到目前为止,它似乎适用于大多数基本情况。数据访问模式取自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);
            }
        }
    }
    

    【讨论】:

    • 它就像一个魅力。我有一个疑问,如果我们有多个读取方法中检索字典的操作,如何在测试中模拟这些操作。我们是否需要以相同的顺序提及模拟对象。
    • 如果您正在测试一种将执行许多数据库操作并且每个都返回相同类型的方法,则此解决方案可能不是最好的。可能能够使用 Rhino Mocks - stackoverflow.com/questions/5609589/… 根据“调用顺序”实现返回不同的值
    • 感谢您的回复。您知道多个数据库操作的任何替代想法吗?我喜欢这种方法的事实是我不需要违反 DRY 原则来创建单独的存储库。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多