如何使用DbContext.Database.SqlQuery <t>的单元测试方法

时间:2017-04-13 11:03:14

标签: c# entity-framework unit-testing

我编写了一个使用DbContext.Database.SqlQuery()(EF6.1)从数据库获取数据的方法:

internal List<WorkOrderLine> Get()
    {
        var sql = GetSql();
        var workOrders = Context.Database.SqlQuery<Model.Analysis.WorkOrder>(sql).ToList();

        return workOrders.Select(workOrder => new WorkOrderLine
        {
            Id = workOrder.a,
            Title = workOrder.b,
            Status = workOrder.c,
            Location = workOrder.d,
            AssignedTo = workOrder.e,
            StartDate = workOrder.f,
            DueDate = workOrder.g,
            CompletedDate = h
        }).ToList();
    }

我想为该方法编写一个单元测试,并检查预期的SQL是否已传递到SQLQuery,然后传回一个数据列表,以便可以通过方法的其余部分对其进行处理,以便我可以检查输出

我相信正确/最好的方法是模拟DbContext并将这个模拟的上下文传递给类而不是真实的上下文。如果是这样,我想我可以通过以下方式之一做到这一点:

  1. 使用Moq

  2. 手动创建模拟以执行测试并返回数据

  3. 使用模拟技术是正确的吗?

    如果是这样,这两个选项中哪一个最简单/最好?

    P.S。我在使用EF进行测试时通常会使用Effort,但它不会处理此方法。

    编辑:

    这是完整的课程:

    internal Report(MyContext context, ChartWidgetFilter filters, string ownerEntityFilter)
                    : base(context, filters, ownerEntityFilter)
                {
                }
    
                internal List<WorkOrderLine> Get()
                {
                    var sql = GetSql();
                    var workOrders = Context.Database.SqlQuery<Model.Analysis.WorkOrder>(sql).ToList();
    
                    return workOrders.Select(workOrder => new WorkOrderLine
                    {
                        Id = workOrder.a,
                        Title = workOrder.b,
                        Status = workOrder.c,
                        Location = workOrder.d,
                        AssignedTo = workOrder.e,
                        StartDate = workOrder.f,
                        DueDate = workOrder.g,
                        CompletedDate = h
                    }).ToList();
                }
    
                private string GetSql()
                {
                    //return the sql generated using the filters and ownerEntityFilter
                    //parameters passed into the constructor
                }
        }
    

    编辑2:

    我需要测试的两件事是:

    1. GetSql()为传递给构造函数的参数创建正确的SQL - ChartWidgetFilter filters, string ownerEntityFilter

    2. return workOrders.Select...语句为WorkOrderLine个对象列表返回正确映射的Model.Analysis.WorkOrder个对象列表

1 个答案:

答案 0 :(得分:0)

以下是关于如何“模拟”单元测试的示例

public class SolvencyBllTest
{
    private MyAttBLL myAttBll;
    private readonly List<AttestationEntity> attestationsFakeForTest = new List<AttestationEntity>
    {
       /// ... Build the child object , used for 'mock'
    }

    //Initialize event => here we define what the 'mock' should to de when we use the [GetListWithChildren] function
    [TestInitialize]
    public void Setup()
    {
        var mockedAttestationFakeToTest = new Mock<IAttestationDataAccessLayer>();
        //setup GetAll : return the whole list
        mockedAttestationFakeToTest
            .Setup(attestation => attestation.GetListWithChildren(It.IsAny<Expression<Func<AttestationEntity, bool>>>()))
            .Returns((Expression<Func<AttestationEntity, bool>> expression) =>
            {
                return this.attestationsFakeForTest.AsQueryable().Where(expression);
            });

        this.myAttBll = new MyAttBLL(attestationsCertificatesRefundDal: null, attestationDal: mockedAttestationFakeToTest.Object, emailNotifier: mockedEmailNotifier.Object);
    }

    [TestMethod]
    public void SolvencyBllTest_CheckAttestation()
    {
        // Initalize the result
        SolvencyCheckResult solvencyCheckResult = new SolvencyCheckResult()
        {
            solvency = new SolvencyModel()
        };

        // Declare and initializes our object which encapsulates our parameters for the solvency check
        SolvencyCheckParameters solvencyCheckParams = new SolvencyCheckParameters(TestConstants.Contact.LAST_NAME, TestConstants.Contact.FIRST_NAME, TestConstants.Contact.BIRTH_DATE, TestConstants.Address.STREET, TestConstants.Address.ZIPCODE, TestConstants.UNIT_TEST_USER);

        // this (solvencyBll) will not try to find in the database but in the collection with just mock before
        // Try to retrieve all certificates dating back 3 months and have the same name + first name + date of birth
        List<AttestationModel> attsLatestToCheck = this.myAttBll.CheckLatestAttestation(solvencyCheckResult, solvencyCheckParams);

        // 1 attestation created today          =>  OK
        // 1 attestation created 1 month ago    =>  OK
        // 1 attestation created 2 month ago    =>  OK
        // 1 attestation created 4 month ago    =>  KO
        Assert.AreEqual(3, attsLatestToCheck.Count);
    }

使用BLL部分函数中的dbContext的示例

public IEnumerable<AttestationEntity> GetListWithChildren(Expression<Func<AttestationEntity, bool>> pred)
    {
        using (ScDbContext context = new ScDbContext())
        {
            return this.GetListWithChildrenInternal(context, pred).OrderBy(att => att.CreatedDate).ToList();
        }
    }

    internal IEnumerable<AttestationEntity> GetListWithChildrenInternal(ScDbContext context, Expression<Func<AttestationEntity, bool>> pred)
    {
        return this.GetListInternal(context, pred, attestationChildren).OrderBy(att => att.CreatedDate).ToList();
    }

internal IEnumerable<E> GetListInternal(DBC context, Expression<Func<E, bool>> where, params Expression<Func<E, object>>[] navigationProperties)
        {
            IQueryable<E> dbQuery = context.Set<E>();

            //Apply eager loading
            foreach (Expression<Func<E, object>> navigationProperty in navigationProperties)
                dbQuery = dbQuery.Include<E, object>(navigationProperty);

            return dbQuery
                //.AsNoTracking() //Don't track any changes for the selected item
                .Where(where)
                .ToList(); //Apply where clause
        }