基于租户的存储库有什么更好的更可测试的方法

What is a better more testable way for Repository based on tenants

我有一个存储库,它接收数据层作为参数和这样的租户 ID。 (代码已简化)

 public class MyRepsitory{

        private readonly IDataAccess _dataAccess;
        private readonly string _tenantID;

        public MyRepsitory(IDataAccess dataAccess, string tenantID)
        {
            _dataAccess = dataAccess;
            _tenantID = tenantID;

        }
}
}

这个存储库还有一个方法 GetClientsForTenantID,它是私有的,这基本上是我的观点,因为每个方法都依赖于 GetClientsForTenantID,这不适合单元测试,因为我不能存根或模拟 GetClientsForTenantID 在我当前的设计中。 这是我目前对 GetClientsForTenantID 方法的签名。 private IEnumerable<string> GetClientsForTenant(string tenantID) 我希望存储库将其作为内部行为来处理,这就是它是私有的原因。我不希望它成为 public 成员,因为它对其他开发人员来说是可变的,而我的 api 将不可靠。我本可以把它变成虚拟的,这样 Mocking Framework 就可以代理它,但这也感觉不对。

它需要注入一些仅用于测试或其他目的的 fakeClients...

这显示了我在代码中遇到的问题。 例如,我无法测试我的 GetProducts() -Method

public IEnumerable<Product> GetProducts(string someParameter){

        var clients = GetClientsForTenant(this._tenantId);

        //do some logic that needs to be tested 
    }

我的另一个想法是注入一个 ITenantStore,基本上是一个 class,负责根据租户 ID 解析客户端。但这还需要获取传入的 IDataAccess 并执行存储库应执行的操作。

像这样:

TenantStore(IDataAccess dataAccess, string tenantID)

MyRepository(IDataAccess dataAccess, ITenantStore tenantStore)

也感觉不对,因为到处都是 IDataAccess...

您推荐什么方法来解决这个问题?有什么提示吗?

编辑:

感谢您的评论,GetClientsForTenant 不在 DataAccess 中,它在存储库中。

我的测试应该是这样的:

var mockMockData = new Mock<IDataAccess>();

        mockMockData.Setup(x=>
            x.ExecuteQuery(It.IsAny<IDictionary<string,object>>(),It.IsAny<string>()))
            .Returns(
               new List<Product>()
            )
            .Callback<IDictionary<string,object>>(
                (parameters,sql)=>{
                    passedInParameters = parameters;

            });
        _repository = new MyRepository(mockMockData.Object,"fakeUser");
var list = _repository.GetProducts();

// Assert the parameters that get's passed into the ExecuteQuery Method because there happens some logic.

因为 GetClientsForTenant 在存储库上并且是私有的,所以它对 Mock IDataAccess 没有帮助。它只会使用不同的参数调用 DataAccess,但来自 MyRepository.GetClientsForTenant 方法。

+1 对 zaitsman 的评论。 您似乎已经有了一个很好的接缝,可以单独测试 MyRepository。

如果 a) IDataAccess 是一个接口并且 b) 您的 GetClientsForTenant 方法在内部使用它,您可以提供一个 returns precooked fakeClients 的假 IDataAccess 并且您将能够完全测试 MyRepository 的逻辑而不暴露任何私有方法。

你的直觉就在这里。一般来说,你不应该为了对某些东西进行单元测试而暴露私有。这就是为什么您应该始终专注于测试公开可见的 behavior 而不是将测试代码绑定到内部实现,如果您试图伪造 GetClientsForTenant 方法,就会这样做。这将导致脆弱的测试,每次您更改一些实现细节时都会中断测试(例如在内部调用 GetClientsForTenant 以外的方法)。

更新:在您上次编辑中您提到: "Since the GetClientsForTenant is on Repository and private it does not help to Mock IDataAccess. It would just call DataAccess with different parameters but from the MyRepository.GetClientsForTenant method."

没错,这就是重点。您的测试不应该关心存储库的内部实现。是否调用 GetClientsForTenant 无关紧要。然而,可能重要的是您的存储库如何与其 IDataAccess 协作者通信。