我用C#,MVC,Entity-Frame,LINQ等编写了一个Web应用程序......现在我想追溯为整个项目创建单元测试。
据我所知,为了编写有效的单元测试,我需要为模型创建存储库,以便可以模拟它们,因此实体框架将对数据库进行任何命中。
我有3个主要型号;类别,密码,UserPassword。
类别包含类别和密码。密码包含UserPasswords(链接到用户帐户)。所以我用基本方法创建了3个存储库,例如;创建,删除,更新等...
但是,看看这个LINQ查询:
var selectedCategoryItem = DatabaseContext.Categories
.Where(c => c.CategoryId == ParentCategoryId)
.Include(c => c.SubCategories)
.Include(c => c.Passwords)
.Include(c => c.Passwords.Select(p => p.Creator))
.ToList()
.Select(c => new Category()
{
SubCategories = c.SubCategories
.Where(sub => !sub.Deleted)
.OrderBy(sub => sub.CategoryOrder)
.ToList(), //make sure only undeleted subcategories are returned
Passwords = c.Passwords
.Where(pass => !pass.Deleted
&& (
(UserPasswordList.Any(up => up.PasswordId == pass.PasswordId && up.Id == UserId))
|| (userIsAdmin && ApplicationSettings.Default.AdminsHaveAccessToAllPasswords)
|| pass.Creator_Id == UserId
)
) //make sure only undeleted passwords - that the current user has acccess to - are returned
.OrderBy(pass => pass.PasswordOrder)
.ToList(),
CategoryId = c.CategoryId,
CategoryName = c.CategoryName,
Category_ParentID = c.Category_ParentID,
CategoryOrder = c.CategoryOrder,
Parent_Category = c.Parent_Category,
Deleted = c.Deleted
})
.SingleOrDefault();
如果存储库只有基本方法,如保存,更新,删除,插入,FindById等....我应该如何将查询分解到存储库?那里还有很多业务逻辑,比如过滤用户可以访问的密码,应该放在哪里?
我读了一些关于返回IQueryable对象的内容,以便可以在服务层中更改LINQ查询......看起来会怎么样?
答案 0 :(得分:2)
如果你想编写单元测试,所有LINQ to Entities都应该在接口后面,
e.g。
public interface ICategoryRepository {
List<Category> GetCategoriesForParentId(int id);
}
然后是一个实现该方法并包含LINQ和过滤逻辑的类。
现在在需要访问它的客户端类中,通过构造函数传递ICategoryRepository
(推荐)。对于单元测试,将模拟的存储库传递给构造函数,该构造函数返回预定义的结果集。
为了测试实际的存储库过滤/转换逻辑,我发现最好的是使用SQLite或SQL CE,并进行集成测试,它加载一个已知状态的预定义数据库(来自保存的副本,或者每次测试都重新创建它,并进行集成测试,将存储库类指向SQLite或SQL CE数据库,而不是真正的存储库类,以便始终处于已知状态,运行查询并检查结果集。
编辑:
将存储库所需的详细信息传递给方法,例如:
List<Category> GetCategoriesForParentIdAnduser(int id, int passwordId, int userId);
或者,由于用户可能在整个网站上,您可以传递
public interface IUserConfig {
int PasswordId{ get; set; }
int UserId { get; set;}
}
// Constructor for CategoryRepository : ICategoryRepository
public CategoryRepository(DbContext context, IUserConfig)
{
....
}
然后
public List<Category> GetCategoriesForParentId(int id){
return _context.Categories
.....
Passwords = c.Passwords.Where(
.....
(UserPasswordList.Any(up => up.PasswordId == _userConfig.PasswordId
&& up.Id == _userConfig.UserId))
.....
}
EDIT2:刚刚注意到.SingleOrDefault()
,而不是返回List<Category>
,您将返回Category
。