简体   繁体   中英

C#/Moq - How to fill with multi-level test data?

I'm new to Moq, and I'd like to write unit tests using it. I have a database with a few tables, like:

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

This is a database on a localdb , which I want to mock. I've created the entities using the Entity Framework. The interface of these entities is IEducationEntities .

Now I'd like to create a mock object and do some testing of some Web Services, like:

    [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");

    }

For this, from what I've understood from the documentation , I should be able to do something like:

[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

However, this doesn't work at all, since the entities generated from the databse do not contain information about subfields apparently, and I get the error:

'DbSet' does not contain a definition for 'UsrPwd' and no extension method 'UsrPwd' accepting a first argument of type 'DbSet' could be found (are you missing a using directive or an assembly reference?)

What am I missing? How to fill a moq object with test data that has the same structure as my database?

This article describes how to mock your Entity Framework context (assuming you're using version 6 or later)

You'll do something like this:

[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 ...
}

It'll probably get annoying to configure the mock DbSet for every entity type for all of your unit tests, so you could make a method to do it.

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;
}

Then your test method becomes

[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 ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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