我一直在尝试统一测试使用存储库模式创建的查询,我似乎无法弄清楚这一点。看来我在这里一定做错了。
因此,我已经创建了一个通用存储库,其外观可能类似于:
public interface IRepository<T> where T:class,IEntity, new()
{
IQueryable<T> Get();
T Get(int id);
T Add(T entity);
T Update(T entity);
void Delete(int id);
}
我还有一个看起来像的上下文:
public class ApplicationContext:DbContext
{
public virtual DbSet<Customer> Customers { get; set; }
public virtual DbSet<User> Users { get; set; }
}
所以现在我当然有了一个看起来像IRepository<Customer> customersRepository.
在我的控制器中,我有一个查询可能类似于:
var customers = _customerRepository
.Get()
.Include(customer => customer.Users)
.Where(customer=>customer.Status == "active")
.ToList();
关于我的问题,我想对此进行测试,但出现错误提示
值不能为空
我的单元测试如下:
[TestMethod]
public void GetCustomerList_ValidParameters_ShouldOnlyReturnItemsWithActiveStatus()
{
//Act
var customers = _customerModel.GetCustomerList(
_parentId, /*parentId*/
string.Empty, /*keywords*/
1, /*page*/
20, /*count*/
out var totalCount, /*totalCount*/
null /*orderBy*/
);
//Assert
Assert.AreEqual(expected:3, actual: customers.Count);
}
我的数据设置如下:
private void AddDataToRepository()
{
var customers = new List<Customer>()
{
new Customer{Id = Guid.NewGuid().ToString(), Name = "ABC", Status = "active", Parent_id = _parentId},
new Customer{Id = Guid.NewGuid().ToString(), Name = "DEF", Status = "canceled", Parent_id = _parentId},
new Customer{Id = Guid.NewGuid().ToString(), Name = "HIJ", Status = "active", Parent_id = _parentId},
new Customer{Id = Guid.NewGuid().ToString(), Name = "MNO", Status = "suspended", Parent_id = _parentId},
new Customer{Id = Guid.NewGuid().ToString(), Name = "QRS", Status = "active", Parent_id = _parentId},
};
var users = new List<User>();
var usersMock = new Mock<DbSet<User>>();
_customerRepositoryMock.Setup(x => x.Get().Include(It.IsAny(typeof(User))));
_customerRepositoryMock.Setup(x => x.Get()).Returns(customers.ToDbSet());
}
如何在这里嘲笑用户。
答案 0 :(得分:1)
您遇到的问题是,当您想在存储库级别进行模拟时,由于您希望模拟出单独的DBSet,因此代码在尝试中变得过于深入,以至于被测代码将与IQueryable返回的期望是与其他模拟DbSet相关的DdSet。
即 给定一个客户包含一个用户:模拟一组期望的客户,模拟一组用户,然后调用代码以某种方式知道他们?
相反,就测试而言,您的存储库是您的边界。该存储库仅返回一个IQueryable<Customer>
,因此模拟的存储库需要将足够多的信息发送回客户集合,以使消费者满意。
private void AddDataToRepository()
{
var testUser = new User{ UserId = 1, UserName = "Me" };
var customers = new List<Customer>()
{
new Customer{Id = Guid.NewGuid().ToString(), Name = "ABC", Status = "active", Parent_id = _parentId, User = testUser},
new Customer{Id = Guid.NewGuid().ToString(), Name = "DEF", Status = "canceled", Parent_id = _parentId, User = testUser},
new Customer{Id = Guid.NewGuid().ToString(), Name = "HIJ", Status = "active", Parent_id = _parentId, User = testUser},
new Customer{Id = Guid.NewGuid().ToString(), Name = "MNO", Status = "suspended", Parent_id = _parentId, User = testUser},
new Customer{Id = Guid.NewGuid().ToString(), Name = "QRS", Status = "active", Parent_id = _parentId, User = testUser},
};
_customerRepositoryMock.Setup(x => x.Get()).Returns(customers.AsQueryable());
}
问题在于您的代码希望在客户上找到用户,因此您需要初始化存根客户以获取用户引用。 (不要期望调用“ Include”知道如何从另一组存根中解决问题)在测试情况下,.Include
调用实际上被忽略了。它在EF中所做的全部工作是通知引擎在执行查询时获取相关数据,实际上并不会加载任何内容。
我还建议您为模拟的Queryable结果阅读实现IDbAsyncQueryProvider
的内容,以便以后可以对存储库方法使用异步操作。 (即ToListAsync()
等)(Unit-testing .ToListAsync() using an in-memory)
答案 1 :(得分:0)
好的,我希望这对其他人有帮助,我实际上无法模拟include方法。因此,我最终要做的是制作一个继承自ICustomerRepository
的{{1}}。然后,我仅添加了一个方法IRepository<Customer>
并对其进行了模拟。