为单元测试创​​建存储库

时间:2014-12-01 23:21:19

标签: c# linq entity-framework unit-testing repository-pattern

我用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查询......看起来会怎么样?

1 个答案:

答案 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