Bagaimana cara mengejek metode dengan objek kode keras?

11

Saya sedang mengerjakan aplikasi yang memiliki banyak lapisan. Lapisan akses data untuk mengambil dan menyimpan data dari sumber data, logika bisnis untuk memanipulasi data, antarmuka pengguna untuk menampilkan data di layar.

Saya juga melakukan pengujian unit pada lapisan logika bisnis. Satu-satunya persyaratan adalah untuk menguji aliran logika lapisan bisnis. Jadi saya menggunakan kerangka kerja Moq untuk mengejek lapisan akses data dan unit menguji lapisan logika bisnis dengan MS Unit.

Saya menggunakan pemrograman antarmuka untuk membuat desain decouple sebanyak mungkin sehingga unit test dapat dilakukan. Lapisan akses data panggilan bisnis lapisan melalui antarmuka.

Saya menghadapi masalah ketika saya mencoba menguji salah satu metode logika bisnis. Metode itu melakukan beberapa pekerjaan dan membuat objek dan meneruskannya ke lapisan akses data. Ketika saya mencoba untuk mengejek metode lapisan akses data maka itu tidak dapat berhasil mengejek

Di sini saya mencoba membuat kode demo untuk menunjukkan masalah saya.

Model:

public class Employee
{
    public string Name { get; set; }
}

Lapisan akses data:

public interface IDal
{
    string GetMessage(Employee emp);
}

public class Dal : IDal
{
    public string GetMessage(Employee emp)
    {
        // Doing some data source access work...

        return string.Format("Hello {0}", emp.Name);
    }
}

Lapisan logika bisnis:

public interface IBll
{
    string GetMessage();
}

public class Bll : IBll
{
    private readonly IDal _dal;

    public Bll(IDal dal)
    {
        _dal = dal;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method.
        Employee emp = new Employee(); 

        string msg = _dal.GetMessage(emp);
        return msg;
    }
}

Tes unit:

[TestMethod]
    public void Is_GetMessage_Return_Proper_Result()
    {
        // Arrange.
        Employee emp = new Employee; // New object.

        Mock<IDal> mockDal = new Mock<IDal>();
        mockDal.Setup(d => d.GetMessage(emp)).Returns("Hello " + emp.Name);

        IBll bll = new Bll(mockDal.Object);

        // Act.

        // This will create another employee object inside the 
        // business logic method, which is different from the 
        // object which I have sent at the time of mocking.
        string msg = bll.GetMessage(); 

        // Assert.
        Assert.AreEqual("Hello arnab", msg);
    }

Dalam unit test case pada saat mengejek saya mengirim objek Karyawan tetapi ketika menjalankan metode logika bisnis, itu menciptakan objek Karyawan yang berbeda di dalam metode. Itu sebabnya saya tidak bisa mengejek objek.

Kalau begitu bagaimana cara mendesain sehingga saya bisa menyelesaikan masalah?

DeveloperArnab
sumber
Biasanya triknya adalah membungkus objek dalam sebuah antarmuka dan membuat semua konsumennya menggunakan antarmuka itu, maka Anda hanya mengejek antarmuka, atau Anda dapat membuat metode virtual dan kemudian moq dapat mengejek metode tanpa antarmuka. Namun tidak yakin tentang rhinomocks atau orang lain dalam kasus ini.
Jimmy Hoffa

Jawaban:

12

Alih-alih membuat Employeeobjek secara langsung dengan menggunakan new, kelas Anda Bllbisa menggunakan EmployeeFactorykelas untuk ini, dengan metode createInstance, yang disuntikkan melalui konstruktor:

 class EmployeeFactory : IEmployeeFactory
 {
       public Employee createInstance(){return new Employee();}
 }

Konstruktor harus mengambil objek pabrik melalui antarmuka IEmployeeFactory, sehingga Anda dapat mengganti pabrik "asli" dengan mudah oleh pabrik tiruan.

public class Bll : IBll
{
    private readonly IDal _dal;
    private readonly IEmployeeFactory _employeeFactory;

    public Bll(IDal dal, IEmployeeFactory employeeFactory)
    {
        _dal = dal;
        _employeeFactory=employeeFactory;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method
        // *** using a factory ***
        Employee emp = _employeeFactory.createObject(); 
        // ...
    }
    //...
}

Pabrik tiruan dapat memberikan tes dengan segala jenis Employeeobjek yang Anda butuhkan untuk pengujian Anda (misalnya, createInstanceselalu dapat mengembalikan objek yang sama):

 class MockEmployeeFactory : IEmployeeFactory
 {
       private Employee _emp;

       public MockEmployeeFactory()
       {
          _emp = new Employee();
          // add any kind of special initializing here for testing purposes
       }

       public Employee createInstance()
       {
          // just for testing, return always the same object
          return _emp;
       }
 }

Sekarang menggunakan tiruan ini dalam pengujian Anda harus melakukan trik.

Doc Brown
sumber
Bisakah Anda memberi saya satu contoh kode, sehingga saya bisa memvisualisasikan teorimu?
DeveloperArnab
@DeveloperArnab: lihat edit saya.
Doc Brown
Sangat membantu ...
DeveloperArnab
4

Saya akan memperlakukannya sebagai satu unit untuk diuji.

Selama Anda mengontrol semua input dari mana Employeeobjek dibuat, fakta bahwa itu dibuat dalam objek yang diuji tidak masalah. Anda hanya perlu metode tiruan untuk mengembalikan hasil yang diharapkan jika konten argumen sesuai dengan harapan.

Jelas itu berarti Anda harus memberikan logika khusus untuk metode tiruan. Logika tingkat lanjut seringkali tidak dapat diuji hanya dengan tipe ejekan "for x return y".

Bahkan, Anda tidak harus membuatnya mengembalikan objek yang berbeda dalam pengujian daripada yang akan diproduksi, karena jika Anda melakukannya, Anda tidak akan menguji kode yang membuatnya. Tetapi kode itu adalah bagian integral dari kode produksi dan oleh karena itu harus dicakup oleh test case juga.

Jan Hudec
sumber
Ya, saya tidak peduli tentang input dari lapisan akses data, saya hanya ingin mengejek objek itu dan mengembalikan data yang dikodekan agar saya dapat menguji logika bisnis. Tetapi masalahnya adalah karena dua objek Karyawan berbeda, saya tidak bisa mengejek metode lapisan akses data.
DeveloperArnab
@DeveloperArnab: Objek akan berbeda, tetapi mereka akan tahu konten. Jadi yang perlu Anda lakukan adalah membuat tiruan melakukan perbandingan kustom alih-alih identitas objek.
Jan Hudec
@DeveloperArnab: Jika Anda menyuntikkan Employeeobjek yang berbeda dalam pengujian, Anda tidak akan menguji kode yang biasanya membuatnya. Jadi Anda tidak harus mengubahnya.
Jan Hudec
0

Ini adalah kegagalan dari beberapa alat pengujian, Anda harus selalu menggunakan antarmuka dan semuanya harus dibuat dengan cara yang memungkinkan Anda untuk menukar objek berbasis antarmuka dengan yang lain.

Namun, ada alat yang lebih baik - ambil Microsoft Fakes (disebut Mole) yang memungkinkan Anda untuk menukar objek apa pun, bahkan yang statis dan global. Dibutuhkan pendekatan yang lebih rendah untuk mengganti objek sehingga Anda tidak harus menggunakan antarmuka di mana-mana sambil tetap menggunakan cara menulis tes yang biasa Anda lakukan.

gbjbaanb
sumber