วิธีการจำลองวิธีด้วยวัตถุที่มีการเข้ารหัสยาก?


11

ฉันกำลังทำงานกับแอพพลิเคชั่นที่มีหลายเลเยอร์ ชั้นการเข้าถึงข้อมูลเพื่อดึงและบันทึกข้อมูลจากแหล่งข้อมูลตรรกะทางธุรกิจเพื่อจัดการข้อมูลส่วนติดต่อผู้ใช้เพื่อแสดงข้อมูลบนหน้าจอ

ฉันยังทำการทดสอบหน่วยของชั้นตรรกะทางธุรกิจด้วย ข้อกำหนดเพียงอย่างเดียวคือการทดสอบการไหลของตรรกะเลเยอร์ธุรกิจ ดังนั้นฉันจึงใช้ Moq framework เพื่อจำลองชั้นการเข้าถึงข้อมูลและหน่วยทดสอบชั้นตรรกะทางธุรกิจด้วยหน่วย MS

ฉันใช้การเขียนโปรแกรมอินเทอร์เฟซเพื่อทำให้การออกแบบลดลงมากที่สุดเพื่อให้การทดสอบหน่วยสามารถทำได้ ชั้นการเข้าถึงข้อมูลชั้นธุรกิจโทรผ่านส่วนต่อประสาน

ฉันกำลังประสบปัญหาเมื่อฉันพยายามทดสอบหนึ่งในวิธีการตรรกะทางธุรกิจ วิธีการนั้นจะทำงานและสร้างวัตถุและส่งต่อไปยัง data access layer เมื่อฉันพยายามที่จะเยาะเย้ยวิธีการเข้าถึงข้อมูลเลเยอร์แล้วก็ไม่สามารถจำลองได้สำเร็จ

ที่นี่ฉันพยายามสร้างรหัสตัวอย่างเพื่อแสดงปัญหาของฉัน

รุ่น:

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

ชั้นการเข้าถึงข้อมูล:

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

ชั้นตรรกะทางธุรกิจ:

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

ทดสอบหน่วย:

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

ในกรณีทดสอบหน่วยในเวลาที่ทำการเยาะเย้ยฉันกำลังส่งออบเจกต์พนักงาน แต่เมื่อเรียกใช้เมธอดตรรกะทางธุรกิจมันกำลังสร้างออบเจ็กต์พนักงานที่แตกต่างกันภายในเมธอด นั่นคือเหตุผลที่ฉันไม่สามารถจำลองวัตถุ

ในกรณีนั้นจะออกแบบเพื่อให้ฉันสามารถแก้ปัญหาได้อย่างไร


โดยปกติแล้วกลอุบายคือการห่อวัตถุในอินเทอร์เฟซและทำให้ผู้บริโภคทั้งหมดใช้อินเทอร์เฟซนั้นจากนั้นคุณเพียงแค่จำลองอินเตอร์เฟสหรือคุณสามารถทำให้วิธีเสมือนเป็นจริงและจากนั้น moq สามารถจำลองวิธีได้โดยไม่ต้องใช้อินเตอร์เฟส อย่างไรก็ตามไม่แน่ใจเกี่ยวกับ rhinomocks หรือคนอื่น ๆ ในกรณีนี้
Jimmy Hoffa

คำตอบ:


12

แทนที่จะสร้างEmployeeวัตถุโดยตรงโดยใช้newคลาสของคุณBllสามารถใช้EmployeeFactoryคลาสสำหรับสิ่งนี้ได้ด้วยวิธีการcreateInstanceซึ่งถูกแทรกเข้าไปใน Constructor:

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

ตัวสร้างควรนำวัตถุของโรงงานผ่านส่วนต่อประสานIEmployeeFactoryดังนั้นคุณสามารถแทนที่โรงงาน "ของจริง" ได้อย่างง่ายดายโดยโรงงานจำลอง

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(); 
        // ...
    }
    //...
}

โรงงานจำลองสามารถให้การทดสอบกับEmployeeวัตถุประเภทใดก็ได้ที่คุณต้องการสำหรับการทดสอบของคุณ (ตัวอย่างเช่นcreateInstanceสามารถส่งคืนวัตถุเดิมได้เสมอ):

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

ตอนนี้ใช้จำลองนี้ในการทดสอบของคุณควรทำเคล็ดลับ


คุณสามารถยกตัวอย่างโค้ดให้ฉันได้ไหมเพื่อฉันจะได้เห็นภาพทฤษฎีของคุณ?
DeveloperArnab

@DeveloperArnab: ดูการแก้ไขของฉัน
Doc Brown

มีประโยชน์มาก ...
DeveloperArnab

4

ฉันจะถือว่าเป็นหน่วยเดียวในการทดสอบ

ตราบใดที่คุณควบคุมอินพุตทั้งหมดที่Employeeวัตถุถูกสร้างขึ้นความจริงที่ว่ามันถูกสร้างขึ้นในวัตถุที่ทดสอบแล้วจะไม่สำคัญ คุณเพียงแค่ต้องใช้วิธีการจำลองเพื่อส่งคืนผลลัพธ์ที่คาดหวังหากเนื้อหาของอาร์กิวเมนต์ตรงกับความคาดหวัง

เห็นได้ชัดว่ามันหมายความว่าคุณต้องให้ตรรกะที่กำหนดเองสำหรับวิธีการจำลอง ตรรกะขั้นสูงมักไม่สามารถทดสอบด้วย mocks ชนิด "for x return y"

ในความเป็นจริงคุณไม่ควรให้มันส่งคืนออบเจ็กต์ที่แตกต่างในการทดสอบมากกว่าที่จะใช้ในการผลิตเพราะถ้าคุณทำคุณจะไม่ทดสอบโค้ดที่ควรสร้าง แต่รหัสนั้นเป็นส่วนสำคัญของรหัสการผลิตดังนั้นจึงควรครอบคลุมในกรณีทดสอบด้วย


ใช่ฉันไม่สนใจเกี่ยวกับอินพุตของ data access layer ฉันแค่อยากจะจำลองวัตถุนั้นและส่งคืนข้อมูลที่เข้ารหัสยากเพื่อให้ฉันสามารถทดสอบตรรกะทางธุรกิจได้ แต่ปัญหาคือเนื่องจากวัตถุพนักงานสองตัวที่แตกต่างกันฉันไม่สามารถจำลองวิธีการเข้าถึงข้อมูลได้
DeveloperArnab

@DeveloperArnab: วัตถุจะแตกต่างกัน แต่พวกเขาจะมีเนื้อหาที่รู้จัก ดังนั้นสิ่งที่คุณต้องทำคือทำให้การเยาะเย้ยเป็นการเปรียบเทียบแบบกำหนดเองแทนที่จะเป็นเอกลักษณ์ของวัตถุ
Jan Hudec

@DeveloperArnab: หากคุณฉีดEmployeeออบเจ็กต์ที่แตกต่างกันในการทดสอบคุณจะไม่ทดสอบโค้ดที่สร้างขึ้นตามปกติ ดังนั้นคุณไม่ควรเปลี่ยนมัน
Jan Hudec

0

มันเป็นความล้มเหลวของเครื่องมือทดสอบบางตัวคุณจะต้องใช้อินเตอร์เฟสและทุกอย่างจะต้องสร้างขึ้นในลักษณะที่ช่วยให้คุณสามารถสลับวัตถุที่อิงกับอินเตอร์เฟสสำหรับเครื่องมืออื่นได้

อย่างไรก็ตามมีเครื่องมือที่ดีกว่า - ใช้ Microsoft Fakes (เรียกว่าไฝ) ที่ช่วยให้คุณสามารถสลับวัตถุใด ๆ แม้กระทั่งวัตถุคงที่และระดับโลก ใช้วิธีการระดับต่ำกว่าเพื่อแทนที่วัตถุดังนั้นคุณไม่จำเป็นต้องใช้อินเทอร์เฟซทุกที่ในขณะที่ยังคงรักษาวิธีการเขียนการทดสอบที่คุณคุ้นเคย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.