C#/Moq - Bagaimana cara mengisi data pengujian bertingkat?

Saya baru mengenal Moq, dan saya ingin menulis unit test menggunakannya. Saya memiliki database dengan beberapa tabel, seperti:

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

Ini adalah database di localdb, yang ingin saya tiru. Saya telah membuat entitas menggunakan Entity Framework. Antarmuka entitas ini adalah IEducationEntities.

Sekarang saya ingin membuat objek tiruan dan melakukan beberapa pengujian terhadap beberapa Layanan Web, seperti:

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

    }

Untuk itu, berdasarkan apa yang saya pahami dari dokumentasi, saya seharusnya mampu melakukan sesuatu seperti:

[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

Namun, ini tidak berhasil sama sekali, karena entitas yang dihasilkan dari database tampaknya tidak berisi informasi tentang subbidang, dan saya mendapatkan kesalahan:

'DbSet' tidak berisi definisi untuk 'UsrPwd' dan tidak ada metode ekstensi 'UsrPwd' yang menerima argumen pertama bertipe 'DbSet' yang dapat ditemukan (apakah Anda melewatkan arahan penggunaan atau referensi Majelis?)

Apa yang saya lewatkan? Bagaimana cara mengisi objek moq dengan data uji yang memiliki struktur yang sama dengan database saya?


person lte__    schedule 20.05.2017    source sumber
comment
Dalam struktur tabel contoh Anda, bidang kata sandi disebut UsrPassword, tetapi Anda menyebutnya UsrPwd dalam kode pengujian/tiruan Anda.   -  person Ben Rubin    schedule 20.05.2017


Jawaban (1)


Artikel ini menjelaskan cara meniru konteks Entity Framework Anda (dengan asumsi Anda menggunakan versi 6 atau lebih baru)

Anda akan melakukan sesuatu seperti ini:

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

Mungkin akan mengganggu untuk mengonfigurasi tiruan DbSet untuk setiap jenis entitas untuk semua pengujian unit Anda, sehingga Anda dapat membuat metode untuk melakukannya.

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

Kemudian metode pengujian Anda menjadi

[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
Pendekatan di atas juga merupakan pendekatan yang akan saya ambil, sampai saya mulai menerapkan pola async/menunggu. Jika Anda membaca tautan yang disediakan, ini menjelaskan bahwa pendekatan ini tidak berhasil untuk itu. Selain itu, pendekatan yang mereka berikan untuk async/await tidak berfungsi dengan EF Core, jadi berhati-hatilah. Saya menggunakan UseInMemoryDatabase EF Core sekarang untuk pengujian unit hingga lapisan repositori dengan banyak keberhasilan. Jika Anda tertarik dengan contohnya, saya akan memberikannya. - person MORCHARD; 23.06.2017
comment
Ya, saya ingin melihat contohnya. - person Ben Rubin; 24.06.2017