คุณช่วยฉันเข้าใจ Moq Callback ได้ไหม


102

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

คุณมีตัวอย่างข้อมูลการทำงานขนาดเล็กที่อธิบายอย่างชัดเจนว่าจะใช้อย่างไรและเมื่อใด

คำตอบ:


84

ยากที่จะเอาชนะhttps://github.com/Moq/moq4/wiki/Quickstart

ถ้ายังไม่ชัดเจนพอฉันจะเรียกว่า doc bug ...

แก้ไข: เพื่อตอบสนองต่อคำชี้แจงของคุณ ...

สำหรับวิธีการเยาะเย้ยแต่ละวิธีที่Setupคุณทำคุณจะต้องระบุสิ่งต่างๆเช่น:

  • ข้อ จำกัด เกี่ยวกับปัจจัยการผลิต
  • ค่าสำหรับ / วิธีที่จะได้รับค่าส่งคืน (ถ้ามี)

.Callbackกลไกการบอกว่า "ฉันไม่สามารถอธิบายได้ในขณะนี้ แต่เมื่อมีการโทรที่มีรูปร่างเช่นนี้เกิดขึ้นโทรหาฉันกลับมาและฉันจะทำสิ่งที่จะต้องทำ" ในฐานะที่เป็นส่วนหนึ่งของห่วงโซ่การโทรที่คล่องแคล่วเดียวกันคุณจะควบคุมผลลัพธ์ที่จะส่งคืน (ถ้ามี) ผ่านทาง.Returns"ในตัวอย่าง QS ตัวอย่างคือทำให้ค่าที่ส่งคืนเพิ่มขึ้นในแต่ละครั้ง

โดยทั่วไปคุณไม่จำเป็นต้องมีกลไกเช่นนี้บ่อยนัก (xUnit Test Patterns มีเงื่อนไขสำหรับการต่อต้านรูปแบบของ ilk Conditional Logic In Tests) และหากมีวิธีที่ง่ายกว่าหรือในตัวในการกำหนดสิ่งที่คุณต้องการก็ควรจะเป็น ใช้ในการตั้งค่า

ส่วนที่ 3 จาก 4 ในซีรีส์ Moq ของ Justin Etheredgeครอบคลุมและมีตัวอย่างการโทรกลับที่นี่

ตัวอย่างง่ายๆของการโทรกลับสามารถพบได้ที่การใช้การโทรกลับด้วยโพสต์Moq


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

19
ยากที่จะเอาชนะ [ลิงค์]? ไม่ใช่เลย. ลิงก์นั้นแสดงวิธีการทำสิ่งต่างๆมากมาย แต่ไม่ได้บอกคุณว่าทำไมคุณถึงต้องทำสิ่งใดสิ่งหนึ่ง ซึ่งเป็นปัญหาทั่วไปในการเยาะเย้ยเอกสารฉันพบ ฉันสามารถนับนิ้วเป็นศูนย์จำนวนคำอธิบายที่ดีและชัดเจนของการเยาะเย้ย TDD + ที่ฉันพบ ส่วนใหญ่ถือว่ามีความรู้ระดับหนึ่งซึ่งถ้าฉันมีฉันก็ไม่จำเป็นต้องอ่านบทความนี้
Ryan Lundy

@ Kyralessa: ฉันเอาประเด็นของคุณ โดยส่วนตัวแล้วฉันมีความรู้เกี่ยวกับหนังสืออยู่บ้างจึงพบว่าสิ่งที่เริ่มต้นอย่างรวดเร็วนั้นสมบูรณ์แบบที่สุด น่าเสียดายที่ฉันไม่ทราบตัวอย่างที่ดีกว่าที่ฉันลิงก์ไว้ในตอนท้ายของโพสต์ หากคุณพบโพสต์ไว้ที่นี่และฉันยินดีที่จะแก้ไขใน (หรือทำ DIY)
Ruben Bartelink

"ฉันจะทำในสิ่งที่ต้องทำและแจ้งให้คุณทราบผลลัพธ์ที่จะส่งคืน (ถ้ามี)" ฉันคิดว่านี่เป็นการทำให้เข้าใจผิด AFAIU Callbackไม่มีส่วนเกี่ยวข้องกับค่าที่ส่งคืน (เว้นแต่คุณจะเชื่อมโยงผ่านโค้ด) โดยทั่วไปแล้วจะทำให้แน่ใจว่าการโทรกลับถูกเรียกก่อนหรือหลังการเรียกแต่ละครั้ง (ขึ้นอยู่กับว่าคุณผูกมัดก่อนหรือหลังReturnsตามลำดับ) ธรรมดาและเรียบง่าย
Ohad Schneider

1
@OhadSchneider ตามลิงค์ของฉัน ... คุณถูกต้อง! สงสัย (แต่ไม่สนใจมากพอเพราะไม่ได้ใช้ Moq เป็นเวลานาน) หากอินเทอร์เฟซ Fluent มีการเปลี่ยนแปลง (ดูเหมือนจะไม่น่าจะเป็นไปได้นั่นคือฉันตั้งสมมติฐานที่ผิดพลาดและไม่ได้อ่านสิ่งที่ฉันเชื่อมโยงด้วยตามปกติ จากการเติมข้อความอัตโนมัติด้วยตัวเอง) หวังว่าการแก้ไขจะตรงจุดของคุณโปรดแจ้งให้เราทราบหากไม่มี
Ruben Bartelink

59

นี่คือตัวอย่างของการใช้การโทรกลับเพื่อทดสอบเอนทิตีที่ส่งไปยังบริการข้อมูลที่จัดการการแทรก

var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback((DataEntity de) => insertedEntity = de);

ไวยากรณ์วิธีการทั่วไปทางเลือก:

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback<DataEntity>(de => insertedEntity = de);

จากนั้นคุณสามารถทดสอบสิ่งต่างๆเช่น

Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");

4
เนื้อหาสำหรับกรณีเฉพาะที่ (ขึ้นอยู่กับว่าคุณกำลังพยายามที่จะแสดงการทดสอบต่อรัฐหรือพฤติกรรม) มันอาจจะในบางกรณีจะทำความสะอาดเพื่อใช้It.Is<T>ในMock.Verifyแทนเกลื่อนทดสอบด้วยอุณหภูมิ แต่ +1 เพราะฉันพนันได้เลยว่ามีคนมากมายที่จะทำงานได้ดีที่สุดจากตัวอย่าง
Ruben Bartelink

11

มีสองประเภทCallbackใน Moq หนึ่งเกิดขึ้นก่อนการโทรกลับ อีกอันเกิดขึ้นหลังจากการโทรกลับ

var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
    .Callback((x, y) =>
    {
        message = "Rally on!";
        Console.WriteLine($"args before returns {x} {y}");
    })
    .Returns(message) // Rally on!
    .Callback((x, y) =>
    {
        message = "Rally over!";
        Console.WriteLine("arg after returns {x} {y}");
    });

ในการโทรกลับทั้งสองครั้งเราสามารถ:

  1. ตรวจสอบข้อโต้แย้งของวิธีการ
  2. อาร์กิวเมนต์วิธีการจับภาพ
  3. เปลี่ยนสถานะตามบริบท

2
จริงๆแล้วทั้งสองอย่างเกิดขึ้นก่อนที่สายจะกลับมา (เท่าที่ผู้โทรเกี่ยวข้อง) ดูstackoverflow.com/a/28727099/67824
Ohad Schneider

8

Callbackเป็นเพียงวิธีการเรียกใช้โค้ดที่กำหนดเองตามที่คุณต้องการเมื่อมีการเรียกใช้วิธีใดวิธีหนึ่งของการจำลอง นี่คือตัวอย่างง่ายๆ:

public interface IFoo
{
    int Bar(bool b);
}

var mock = new Mock<IFoo>();

mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
    .Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
    .Returns(42);

var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);

// output:
// Bar called with: True
// Result: 42

ฉันเพิ่งพบกรณีการใช้งานที่น่าสนใจสำหรับมัน สมมติว่าคุณคาดหวังว่าจะมีการเรียกร้องให้ล้อเลียนของคุณ แต่ก็เกิดขึ้นพร้อมกัน ดังนั้นคุณจึงไม่มีทางรู้ลำดับที่พวกเขาเรียก แต่คุณต้องการทราบว่ามีการโทรที่คุณคาดไว้ (โดยไม่คำนึงถึงลำดับ) คุณสามารถทำสิ่งนี้:

var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));

// output:
// Invocations: True, False

BTW อย่าสับสนกับความแตกต่าง"ก่อนReturns" และ "หลังReturns" ที่ทำให้เข้าใจผิด เป็นเพียงความแตกต่างทางเทคนิคว่าโค้ดที่กำหนดเองของคุณจะทำงานหลังจากReturnsได้รับการประเมินหรือก่อนหน้านี้ ในสายตาของผู้เรียกทั้งสองจะทำงานก่อนที่ค่าจะถูกส่งกลับ อันที่จริงหากวิธีนี้เป็นการvoidคืนค่าคุณไม่สามารถโทรReturnsได้ แต่ก็ยังใช้งานได้เหมือนเดิม สำหรับข้อมูลเพิ่มเติมโปรดดูที่https://stackoverflow.com/a/28727099/67824


1

ด้านบนของคำตอบที่ดีอื่น ๆ ที่นี่ฉันเคยใช้เพื่อแสดงตรรกะก่อนที่จะโยนข้อยกเว้น ตัวอย่างเช่นฉันต้องการจัดเก็บวัตถุทั้งหมดที่ส่งผ่านไปยังวิธีการเพื่อการตรวจสอบในภายหลังและวิธีการนั้น (ในการทดสอบบางกรณี) จำเป็นต้องทิ้งข้อยกเว้น Calling .Throws(...)บนMock.Setup(...)แทนที่Callback()การกระทำและไม่เคยเรียกมันว่า อย่างไรก็ตามด้วยการทิ้งข้อยกเว้นไว้ใน Callback คุณยังคงสามารถทำสิ่งดีๆทั้งหมดที่การโทรกลับมีให้และยังคงมีข้อยกเว้น

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