ฉันจะโมควิธีการที่มีอาร์กิวเมนต์ที่เป็นทางเลือกในลายเซ็นโดยไม่ระบุอย่างชัดเจนหรือใช้โอเวอร์โหลดได้อย่างไร


119

รับอินเทอร์เฟซต่อไปนี้:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

พยายามล้อเลียนโดยใช้ Moq:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

ให้ข้อผิดพลาดต่อไปนี้ในเวลาคอมไพล์:

แผนภูมินิพจน์ต้องไม่มีการเรียกหรือการเรียกใช้ที่ใช้อาร์กิวเมนต์ที่เป็นทางเลือก

ฉันพบปัญหาข้างต้นที่ยกขึ้นเป็นการปรับปรุงในรายการปัญหาของ Moq และดูเหมือนว่าจะถูกกำหนดให้กับรุ่น 4.5 (ทุกครั้งที่เป็น)

คำถามของฉันคือ: ฉันควรทำอย่างไรหากข้างต้นจะไม่ได้รับการแก้ไขในเร็ว ๆ นี้? ตัวเลือกของฉันเป็นเพียงการตั้งค่าเริ่มต้นของพารามิเตอร์ทางเลือกอย่างชัดเจนทุกครั้งที่ฉันเยาะเย้ย (ประเภทใดที่เอาชนะจุดที่ระบุไว้ตั้งแต่แรก) หรือสร้างโอเวอร์โหลดโดยไม่ต้องบูล (เช่นสิ่งที่ฉันทำ ก่อน C # 4)?

หรือมีใครเจอวิธีที่ฉลาดกว่านี้ในการเอาชนะปัญหานี้หรือไม่?


5
มันสมเหตุสมผลไหมที่จะระบุ It.IsAny <bool> () สำหรับพารามิเตอร์ที่สอง
Paul d'Aoust

หนึ่งปีครึ่งหลังจากนี้ยังคงเป็นจริง ..
มูกัส

@ มูคัสอย่าลังเลที่จะลงประชาสัมพันธ์ครับ
IamDOM

คำตอบ:


91

ฉันเชื่อว่าทางเลือกเดียวของคุณในตอนนี้คือการรวมboolพารามิเตอร์ไว้ในการตั้งค่าสำหรับFoo.

ฉันไม่คิดว่ามันจะเอาชนะจุดประสงค์ของการระบุค่าเริ่มต้น ค่าดีฟอลต์คือความสะดวกในการโทรหารหัส แต่ฉันคิดว่าคุณควรมีความชัดเจนในการทดสอบของคุณ สมมติว่าคุณไม่ต้องระบุboolพารามิเตอร์ จะเกิดอะไรขึ้นถ้าในอนาคตมีคนเปลี่ยนค่าเริ่มต้นของbto true? นี้จะนำไปสู่ความล้มเหลวในการทดสอบ (และชอบธรรมดังนั้น) แต่พวกเขาจะเป็นเรื่องยากมากขึ้นที่จะแก้ไขเพราะสมมติฐานที่ซ่อนที่เป็นb falseการระบุboolพารามิเตอร์อย่างชัดเจนมีประโยชน์อีกประการหนึ่งคือช่วยเพิ่มความสามารถในการอ่านการทดสอบของคุณ ใครบางคนที่เดินผ่านพวกเขาจะรู้ได้อย่างรวดเร็วว่ามีFooฟังก์ชันหนึ่งที่รับสองพารามิเตอร์ นั่นคือ 2 เซนต์ของฉันอย่างน้อย :)

สำหรับการระบุทุกครั้งที่คุณล้อเลียนอย่าสร้างรหัสซ้ำ: สร้างและ / หรือเริ่มต้นการจำลองในฟังก์ชันเพื่อให้คุณมีการเปลี่ยนแปลงเพียงจุดเดียว หากคุณต้องการจริงๆคุณสามารถเอาชนะการมาสั้น ๆ ของ Moq ได้ที่นี่โดยการทำซ้ำFooพารามิเตอร์ในฟังก์ชันการเริ่มต้นนี้:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}

1
คำตอบที่ยอดเยี่ยม; ฉันได้ดำเนินการต่อไปแล้วและระบุไว้อย่างชัดเจนในการล้อเลียนของฉัน แต่คำตอบของคุณยืนยันอย่างชัดเจนและมีเหตุผลว่าทำไมฉันจึงควรทำเช่นนี้ ขอบคุณ @Chris
Appulus

9
การเปลี่ยนพารามิเตอร์เริ่มต้น "ควร" ทำลายการทดสอบ การไม่มีการทดสอบล้มเหลวเมื่อมีการเปลี่ยนแปลงค่าเริ่มต้นอาจเป็นสัญญาณของการทดสอบที่ไม่ดี รหัสอาจใช้ค่าเริ่มต้น แต่การทดสอบไม่ได้?
Pop Catalin

ใช้มาระยะหนึ่งแล้ว แต่ฉันได้ลองใช้วิธีนี้กับ Moq เมื่อพยายามล้อเลียนอินเทอร์เฟซ (IDConnection ใน Dapper) และฉันยังคงได้รับข้อผิดพลาดเดียวกัน ความคิดใด ๆ ทำไม? บรรทัดตัวอย่าง: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)) ผลตอบแทน (รายการใหม่ <MyObject> ()); สองค่าสุดท้ายเป็นพารามิเตอร์ทางเลือกในวิธีที่ฉันกำลังตั้งค่า
Raelshark

4
Arrgggh! if (!x) {} else {}รูปแบบการต่อต้านที่น่ากลัว:)
nicodemus13

1
@ nicodemus13 ใช่ แต่ฉันพยายามทำให้ตัวอย่างโค้ดใกล้เคียงกับตัวอย่างของ OP ในคำถามมากที่สุด ฉันไม่จำเป็นต้องสนับสนุนมัน :)
Chris Mantle

8

เพิ่งพบปัญหานี้ในวันนี้ Moq ไม่รองรับกรณีการใช้งานนี้ ดังนั้นดูเหมือนว่าการลบล้างวิธีการนี้จะเพียงพอสำหรับกรณีนี้

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

ตอนนี้มีทั้งสองวิธีแล้วและตัวอย่างนี้จะใช้งานได้:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

2

การใช้ Moq เวอร์ชัน 4.10.1 ฉันสามารถทำสิ่งต่อไปนี้ได้

ด้วยอินเทอร์เฟซ:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

และ Mock

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);

แก้ไขการเรียกไปที่ Foo โดยให้พารามิเตอร์แรกถูกต้อง

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