单元测试EF-如何从BL中提取EF代码? [英] Unit testing EF - how to extract EF code out from BL?

查看:74
本文介绍了单元测试EF-如何从BL中提取EF代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于一件事,我已经读了太多了(数十篇文章)

I have read so much (dozens of posts) about one thing:

如何对业务逻辑代码进行单元测试

我有3层WCF服务:


  • 服务层

  • 业务逻辑层

  • 数据访问层

我的业务逻辑对所有数据库操作使用 DbContext
我所有的实体现在都是POCO(以前是ObjectContext,但是我更改了它)。

My business logic uses the DbContext for all the database operations. All my entities are now POCOs (used to be ObjectContext, but I changed that).

我已阅读 Ladislav Mrnka的答案此处此处为什么我们不应该模拟\伪造 DbContext 的原因。

I have read Ladislav Mrnka's answer here and here on the reasons why we should not mock \ fake the DbContext.

他说:
这就是为什么我认为处理上下文/ Linq-to-entities的代码应该包含集成测试并针对实际数据库进行工作的原因。

和:
当然,您的方法在某些情况下可行,但是单元测试策略必须在所有情况下均有效-要使其奏效,您必须将EF和IQueryable完全从已测试的方法移开。

我的问题是-您如何实现这一目标?

My question is - how do you achieve this ???

public class TaskManager
{
    public void UpdateTaskStatus(
        Guid loggedInUserId,
        Guid clientId,
        Guid taskId,
        Guid chosenOptionId,
        Boolean isTaskCompleted,
        String notes,
        Byte[] rowVersion
    )
    {
        using (TransactionScope ts = new TransactionScope())
        {
            using (CloseDBEntities entities = new CloseDBEntities())
            {
                User currentUser = entities.Users.SingleOrDefault(us => us.Id == loggedInUserId);
                if (currentUser == null)
                    throw new Exception("Logged user does not exist in the system.");

                // Locate the task that is attached to this client
                ClientTaskStatus taskStatus = entities.ClientTaskStatuses.SingleOrDefault(p => p.TaskId == taskId && p.Visit.ClientId == clientId);
                if (taskStatus == null)
                    throw new Exception("Could not find this task for the client in the database.");

                if (taskStatus.Visit.CustomerRepId.HasValue == false)
                    throw new Exception("No customer rep is assigned to the client yet.");
                TaskOption option = entities.TaskOptions.SingleOrDefault(op => op.Id == optionId);
                if (option == null)
                    throw new Exception("The chosen option was not found in the database.");

                if (taskStatus.RowVersion != rowVersion)
                    throw new Exception("The task was updated by someone else. Please refresh the information and try again.");

                taskStatus.ChosenOptionId = optionId;
                taskStatus.IsCompleted = isTaskCompleted;
                taskStatus.Notes = notes;

                // Save changes to database
                entities.SaveChanges();
            }

            // Complete the transaction scope
            ts.Complete();
        }
    }
}

在随附的代码中我的业务逻辑对功能的演示。
该函数有多个行到数据库。
我不知道如何精确地将EF代码从该函数中提取出来,并分离到单独的程序集中,以便能够对该功能进行单元测试 (通过注入一些伪造的数据而不是EF数据),并集成测试包含 EF函数的程序集。

In the code attached there is a demonstration of a function from my business logic. The function has several 'trips' to the database. I don't understand how exactly I can strip the EF code from this function out to a separate assembly, so that I am able to unit test this function (by injecting some fake data instead of the EF data), and integrate test the assembly that contains the 'EF functions'.

Can Ladislav还是其他人帮忙?

Can Ladislav or anyone else help out?

这里是另一个示例我的业务逻辑中的代码,我不明白如何从测试的方法中移出EF和IQueryable代码:

Here is another example of code from my business logic, I don't understand how I can 'move the EF and IQueryable code' out from my tested method :

public List<UserDto> GetUsersByFilters(
    String ssn, 
    List<Guid> orderIds, 
    List<MaritalStatusEnum> maritalStatuses, 
    String name, 
    int age
)
{
    using (MyProjEntities entities = new MyProjEntities())
    {
        IQueryable<User> users = entities.Users;

        // Filter By SSN (check if the user's ssn matches)
        if (String.IsNullOrEmusy(ssn) == false)
            users = users.Where(us => us.SSN == ssn);

        // Filter By Orders (check fi the user has all the orders in the list)
        if (orderIds != null)
            users = users.Where(us => UserContainsAllOrders(us, orderIds));

        // Filter By Marital Status (check if the user has a marital status that is in the filter list)
        if (maritalStatuses != null)
            users = users.Where(pt => maritalStatuses.Contains((MaritalStatusEnum)us.MaritalStatus));

        // Filter By Name (check if the user's name matches)
        if (String.IsNullOrEmusy(name) == false)
            users = users.Where(us => us.name == name);

        // Filter By Age (check if the user's age matches)
        if (age > 0)
            users = users.Where(us => us.Age == age);


        return users.ToList();
    }
}

private   Boolean   UserContainsAllOrders(User user, List<Guid> orderIds)
{
    return orderIds.All(orderId => user.Orders.Any(order => order.Id == orderId));
}


推荐答案

如果要您的 TaskManager 类的单元测试,您应该采用 Repository dessign模式,并将诸如UserRepository或ClientTaskStatusRepository之类的存储库注入此类。然后,您将使用这些存储库并调用其方法,而不是构造 CloseDBEntities 对象,例如:

If you want to unit test your TaskManager class, you should employ the Repository dessign pattern and inject repositories such as UserRepository or ClientTaskStatusRepository into this class. Then instead of constructing CloseDBEntities object you will use these repositories and call their methods, for example:

User currentUser = userRepository.GetUser(loggedInUserId);
ClientTaskStatus taskStatus = 
    clientTaskStatusRepository.GetTaskStatus(taskId, clientId);

如果您想集成测试,请选择 TaskManager 类,解决方案要简单得多。您只需要使用指向测试数据库的连接字符串来初始化 CloseDBEntities 对象即可。实现此目的的一种方法是将 CloseDBEntities 对象注入 TaskManager 类。

If yout wanto to integration test your TaskManager class, the solution is much more simple. You just need to initialize CloseDBEntities object with a connection string pointing to the test database and that's it. One way how to achieve this is injecting the CloseDBEntities object into the TaskManager class.

在每次集成测试运行之前,您还需要重新创建测试数据库,并用一些测试数据填充它。可以使用数据库初始化程序

You will also need to re-create the test database before each integration test run and populate it with some test data. This can be achieved using Database Initializer.

这篇关于单元测试EF-如何从BL中提取EF代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆