简体   繁体   English

创建用于单元测试的存储库

[英]Creating repositories for Unit Testing

I've written a web application in C#, MVC, Entity-Frame, LINQ etc... and now I want to retroactively create Unit tests for the whole project. 我已经用C#,MVC,实体框架,LINQ等编写了一个Web应用程序,现在我想为整个项目追溯创建单元测试。

I understand that in order to write effective Unit Tests, I need to create repositories for the models, so that they can be mocked, and thus entity framework will be do any hits on the database. 我知道,为了编写有效的单元测试,我需要为模型创建存储库,以便可以对它们进行模拟,因此实体框架将对数据库产生任何影响。

I have 3 main models; 我有3个主要模型; Category, Password, UserPassword. 类别,密码,UserPassword。

Categories contain categories and passwords. 类别包含类别和密码。 And passwords contain UserPasswords (which link to User accounts). 密码包含UserPasswords(链接到用户帐户)。 So I've created 3 repositories with the basic methods such as; 因此,我用基本方法创建了3个存储库,例如; Create, Delete, Update etc... 创建,删除,更新等...

However, looking at this LINQ query: 但是,查看此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();

If the repositories just have basic methods like Save, Update, Delete, Insert, FindById etc.... how should I break the query up into the repositories? 如果存储库只有基本方法,如保存,更新,删除,插入,FindById等...,我应该如何将查询分解到存储库中? There's also a lot of business logic in there such as filtering for passwords that the user has access to, where should that sit? 那里还有很多业务逻辑,例如过滤用户可以访问的密码,该密码应该放在哪里?

I read something about returning IQueryable objects, so that LINQ queries can be changed in the service layer... how would that look? 我读了一些有关返回IQueryable对象的内容,以便可以在服务层中更改LINQ查询...看起来如何?

If you want to write unit tests, all of that LINQ to Entities, should be behind an Interface, 如果您要编写单元测试,则所有LINQ to Entities都应位于接口后面,

eg 例如

public interface ICategoryRepository {
    List<Category> GetCategoriesForParentId(int id);
}

then a class which implements that method and contains the LINQ and filtering logic. 然后是一个实现该方法并包含LINQ和过滤逻辑的类。

Now in the client classes which need access to it, pass in an ICategoryRepository via the constructor (recommended). 现在,在需要访问它的客户端类中,通过构造函数传递ICategoryRepository (推荐)。 And for Unit Testing, pass in a mocked repository into the constructor which returns a pre-defined result set. 对于单元测试,将模拟的存储库传递到构造函数中,该构造函数将返回预定义的结果集。

For testing the actual repository filtering/transforming logic, what i found best, was to use SQLite or SQL CE, and have an integration test, which loads up a pre-defined database, which is in a known state (either from a saved copy, or re-creating it each test) and have an integration tests, pointing the repository class at the SQLite or SQL CE database, rather than your real one so that is always in a known state, run the query, and check the result set. 为了测试实际的存储库筛选/转换逻辑,我发现最好的方法是使用SQLite或SQL CE,并进行集成测试,以加载处于已知状态的预定义数据库(从保存的副本中加载) ,或在每个测试中重新创建它)并进行集成测试,将存储库类指向SQLite或SQL CE数据库,而不是您的真实类,以便始终处于已知状态,运行查询并检查结果集。

EDIT: 编辑:

Either pass in the details needed by the repository into the method, eg: 将存储库所需的详细信息传递到方法中,例如:

List<Category> GetCategoriesForParentIdAnduser(int id, int passwordId, int userId);

Or, as User is probably across the site, you could pass a 或者,由于用户可能遍布整个网站,因此您可以通过

public interface IUserConfig {
     int PasswordId{ get; set; }
     int UserId { get; set;}
}


// Constructor for CategoryRepository : ICategoryRepository 
public CategoryRepository(DbContext context, IUserConfig)  
{
   ....
}

then 然后

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: Just noticed the .SingleOrDefault() , than rather than returning List<Category> you would return Category . EDIT2:刚注意到.SingleOrDefault() ,而不是返回List<Category> ,而不是返回Category

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM