C#/Moq - จะกรอกข้อมูลทดสอบหลายระดับได้อย่างไร

ฉันยังใหม่กับ Moq และฉันต้องการเขียนการทดสอบหน่วยโดยใช้มัน ฉันมีฐานข้อมูลที่มีตารางไม่กี่ตาราง เช่น:

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

นี่คือฐานข้อมูลบน localdb ซึ่งฉันต้องการจำลอง ฉันได้สร้างเอนทิตีโดยใช้ Entity Framework อินเทอร์เฟซของเอนทิตีเหล่านี้คือ IEducationEntities

ตอนนี้ฉันต้องการสร้างวัตถุจำลองและทดสอบบริการทางเว็บบางอย่าง เช่น:

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

    }

สำหรับสิ่งนี้ จากสิ่งที่ฉันเข้าใจจากเอกสารประกอบ ฉันควรจะ สามารถทำอะไรบางอย่างเช่น:

[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

อย่างไรก็ตาม สิ่งนี้ไม่ได้ผลเลย เนื่องจากเอนทิตีที่สร้างจาก databse ไม่มีข้อมูลเกี่ยวกับฟิลด์ย่อยอย่างชัดเจน และฉันได้รับข้อผิดพลาด:

'DbSet' ไม่มีคำจำกัดความสำหรับ 'UsrPwd' และไม่มีวิธีการขยาย 'UsrPwd' ที่ยอมรับอาร์กิวเมนต์แรกของประเภท 'DbSet' (คุณขาดคำสั่งการใช้หรือการอ้างอิงแอสเซมบลีหรือไม่)

ฉันพลาดอะไรไป? จะเติมวัตถุ moq ด้วยข้อมูลทดสอบที่มีโครงสร้างเดียวกันกับฐานข้อมูลของฉันได้อย่างไร


person lte__    schedule 20.05.2017    source แหล่งที่มา
comment
ในโครงสร้างตารางตัวอย่างของคุณ ฟิลด์รหัสผ่านเรียกว่า UsrPassword แต่คุณเรียกว่า UsrPwd ในโค้ดทดสอบ/จำลองของคุณ   -  person Ben Rubin    schedule 20.05.2017


คำตอบ (1)


บทความนี้อธิบายวิธีการจำลองบริบท Entity Framework ของคุณ (สมมติว่าคุณใช้เวอร์ชัน 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 ...
}
person Ben Rubin    schedule 20.05.2017
comment
วิธีข้างต้นเคยเป็นแนวทางที่ฉันจะใช้เช่นกัน จนกระทั่งฉันเริ่มใช้รูปแบบ async/await หากคุณอ่านลิงก์ที่ให้มาจะอธิบายว่าวิธีนี้ใช้ไม่ได้ผล นอกจากนี้ วิธีการที่พวกเขาให้ไว้สำหรับ async/await ไม่สามารถใช้ได้กับ EF Core ดังนั้นโปรดระวัง ตอนนี้ฉันใช้ UseInMemoryDatabase ของ EF Core สำหรับการทดสอบหน่วยลงไปจนถึงเลเยอร์พื้นที่เก็บข้อมูลและประสบความสำเร็จอย่างมาก หากคุณสนใจตัวอย่าง ฉันจะจัดเตรียมไว้ให้ - person MORCHARD; 23.06.2017
comment
ใช่ ฉันอยากเห็นตัวอย่าง - person Ben Rubin; 24.06.2017