C#/ Moq - 如何填写多级测试数据?

时间:2017-05-20 18:23:45

标签: c# entity-framework unit-testing moq

我是Moq的新手,我想用它来编写单元测试。我有一个包含几个表的数据库,例如:

EducationUser   | Application
- UsrName         - Student
- UsrPwd          - CourseId
- UsrChallenge    - Date
- IsTeacher       - Grade
- FullName

这是localdb上的数据库,我想模拟它。我使用实体框架创建了实体。这些实体的接口是IEducationEntities

现在我想创建一个模拟对象并对某些Web服务进行一些测试,例如:

    [TestMethod()]
    public void LoginTest()
    {
        HttpResponseMessage response = Request.CreateResponse(_accountController.Login("andrew", "DefaultPassword"));
        Assert.IsTrue(response.IsSuccessStatusCode, "User unable to log in with correct login info");

    }

为此,根据我对documentation的理解,我应该可以做类似的事情:

[TestClass()]
public class AccountControllerTests : ApiController
{
    Mock<IEducationEntities> _entities = new Mock<IEducationEntities>(MockBehavior.Strict);
    private AccountController _accountController;

public AccountControllerTests() {
        _accountController = new AccountController(_entities.Object);
        _entities.Setup(table => table.EducationUsers.UsrName).Returns("andrew");
        _entities.Setup(table => table.EducationUsers.UsrPwd).Returns("DefaultPassword");
}
[TestMethod] //etc, defining tests below

然而,这根本不起作用,因为从数据库生成的实体显然不包含有关子字段的信息,我收到错误:

  

'DbSet'不包含'UsrPwd'的定义   没有扩展方法'UsrPwd'接受类型的第一个参数   可以找到'DbSet'(你是否错过了使用   指令或程序集引用?)

我错过了什么?如何使用与我的数据库具有相同结构的测试数据填充moq对象?

1 个答案:

答案 0 :(得分:2)

This article describes how to mock your Entity Framework context(假设您使用的是版本6或更高版本)

你会做这样的事情:

[TestMethod]
public void TestSomething()    
{
   // Create the user data
   var educationUsers = new List<EducationUser>
   {
       new EducationUser
       {
           UsrName = "andrew",
           UsrPwd = "DefaultPassword"
       }
   }.AsQueryable();

   // Create the DbSet that contains the user data and wire it up to return the user data that was created above
   Mock<DbSet<EducationUser>> educationUsersDbSet = new Mock<DbSet<EducationUser>>();
   educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.Provider).Returns(educationUsers.Provider);
   educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.Expression).Returns(educationUsers.Expression);
   educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.ElementType).Returns(educationUsers.ElementType);
   educationUsersDbSet.As<IQueryable<EducationUser>>().Setup(m => m.GetEnumerator()).Returns(educationUsers.GetEnumerator());

   // Create the mock context and wire up its EducationUsers property to return the DbSet that was created above
   var context = new Mock<IEducationEntities>();
   context.Setup(e => e.EducationUsers).Returns(educationUsersDbSet.Object);

   // Create the account controller using the mock DbContext
   _accountController = new AccountController(context.Object);

   // ... the rest of your testing code ...
}

为所有单元测试的每个实体类型配置模拟DbSet可能会很烦人,因此您可以创建一个方法来完成它。

public static Mock<DbSet<TEntity>> CreateMockDbSet<TEntity>(IQueryable<TEntity> models) where TEntity : class
{
    Mock<DbSet<TEntity>> dbSet = new Mock<DbSet<TEntity>>();

    dbSet.As<IQueryable<TEntity>>().Setup(e => e.ElementType).Returns(models.ElementType);
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.Expression).Returns(models.Expression);
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.GetEnumerator()).Returns(models.GetEnumerator());
    dbSet.As<IQueryable<TEntity>>().Setup(e => e.Provider).Returns(models.Provider);

    return dbSet;
}

然后您的测试方法变为

[TestMethod]
public void TestSomething()    
{
   // Create the user data
   var educationUsers = new List<EducationUser>
   {
       new EducationUser
       {
           UsrName = "andrew",
           UsrPwd = "DefaultPassword"
       }
   }.AsQueryable();

   // Create the DbSet that contains the user data and wire it up to return the user data that was created above
   Mock<DbSet<EducationUser>> educationUsersDbSet = new CreateMockDbSet(educationUsers);

   // Create the mock context and wire up its EducationUsers property to return the DbSet that was created above
   var context = new Mock<IEducationEntities>();
   context.Setup(e => e.EducationUsers).Returns(educationUsersDbSet.Object);

   // Create the account controller using the mock DbContext
   _accountController = new AccountController(context.Object);

   // ... the rest of your testing code ...
}