เหตุใดการยืนยันนี้จึงทำให้เกิดข้อยกเว้นของรูปแบบเมื่อเปรียบเทียบโครงสร้าง


94

ฉันพยายามยืนยันความเท่าเทียมกันของสองSystem.Drawing.Sizeโครงสร้างและฉันได้รับข้อยกเว้นของรูปแบบแทนการยืนยันความล้มเหลวที่คาดไว้

[TestMethod]
public void AssertStructs()
{
    var struct1 = new Size(0, 0);
    var struct2 = new Size(1, 1);

    //This throws a format exception, "System.FormatException: Input string was not in a correct format."
    Assert.AreEqual(struct1, struct2, "Failed. Expected {0}, actually it is {1}", struct1, struct2); 

    //This assert fails properly, "Failed. Expected {Width=0, Height=0}, actually it is {Width=1, Height=1}".
    Assert.AreEqual(struct1, struct2, "Failed. Expected " + struct1 + ", actually it is " + struct2); 
}

เป็นพฤติกรรมที่ตั้งใจหรือไม่? ฉันทำอะไรผิดที่นี่?


คุณได้ลองใช้Assert.AreEqual(struct1, struct2, string.Format("Failed expected {0} actually is {1}struct1 ToString (), struct2 ToString ())) `หรือไม่
DiskJunky

ทำงานได้ดี อย่างไรก็ตามฉันสงสัยว่าทำไม Assert.AreEqual () ไม่สามารถจัดรูปแบบสตริงด้วยประเภทโครงสร้างได้
Kyle

@Kyle ด้วยความอยากรู้อยากเห็นนี่ไม่ใช่กับกรอบการทดสอบหน่วยรุ่นที่เข้ากันได้กับ Silverlight ใช่หรือไม่? ฉันสามารถทำซ้ำได้ด้วย DLL เหล่านั้น (ยังไม่ได้ลอง. NET framework เวอร์ชันเต็ม) แก้ไข: ไม่เป็นไรทดสอบกับไฟล์เต็มแล้วก็ยังล้มเหลว :)
Chris Sinclair

@ChrisSinclair ไม่นี่ใช้ mstest รุ่นใดก็ได้ที่มาพร้อมกับ Visual Studio 2010 ultimate โครงการทดสอบเองกำหนดเป้าหมาย. NET Framework 4
Kyle

4
ไม่แน่ใจว่าคุณให้คำด่าหรือเปล่า แต่ใช้งานได้ดีใน NUnit ฉันได้เห็น "ปัญหา" เช่นนี้ใน MStest NUnit ดูเป็นผู้ใหญ่ขึ้นเล็กน้อย (อย่างน้อยก็สำหรับฉัน) +1 สำหรับโพสต์
bas

คำตอบ:


100

ฉันได้รับมัน. และใช่มันเป็นจุดบกพร่อง

ปัญหาคือมีสองระดับที่string.Formatเกิดขึ้นที่นี่

แรกระดับของการจัดรูปแบบเป็นสิ่งที่ต้องการ:

string template  = string.Format("Expected: {0}; Actual: {1}; Message: {2}",
                                 expected, actual, message);

จากนั้นเราจะใช้string.Formatกับพารามิเตอร์ที่คุณให้มา:

string finalMessage = string.Format(template, parameters);

(เห็นได้ชัดว่ามีการจัดเตรียมวัฒนธรรมและการฆ่าเชื้อบางอย่าง ... แต่ยังไม่เพียงพอ)

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

{Width=0, Height=0}

ดังนั้นการจัดรูปแบบระดับที่สองจึงเป็นดังนี้:

string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " +
              "Message = Failed expected {0} actually is {1}", struct1, struct2);

... และนั่นคือสิ่งที่ล้มเหลว อุ๊ย.

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

var x = "{0}";
var y = "{1}";
Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar");

ผลลัพธ์คือ:

Assert.AreEqual failed. Expected:<foo>. Actual:<bar>. What a surprise!

แตกอย่างชัดเจนโดยที่เราไม่ได้คาดหวังfooและไม่ได้เป็นมูลค่าที่แท้จริงbar!

โดยทั่วไปนี้เป็นเหมือนการโจมตีฉีด SQL string.Formatแต่ในบริบทที่ค่อนข้างน่ากลัวน้อยลง

ในการแก้ปัญหาเบื้องต้นคุณสามารถใช้string.Formatตามที่ StriplingWarrior แนะนำ ซึ่งจะหลีกเลี่ยงการจัดรูปแบบระดับที่สองซึ่งเป็นผลมาจากการจัดรูปแบบด้วยค่าจริง / ที่คาดไว้


ขอบคุณสำหรับคำตอบโดยละเอียด Jon! ฉันจบลงด้วยการใช้ StriplingWarriors แก้ไข
Kyle

1
ไม่มี%*nเทียบเท่า? :(
Tom Hawtin - แทคไลน์

มีใครส่งรายงานข้อบกพร่องสำหรับสิ่งนี้หรือไม่
Kevin

@ เควิน: ใช่ - แม้ว่าจะเป็นการภายในดังนั้นฉันไม่แน่ใจว่าความคืบหน้าจะปรากฏต่อสาธารณะหรือไม่จนกว่าจะได้รับการแก้ไข
Jon Skeet

1
@ เควินฉันใส่หนึ่งใน MS เช่นกันเมื่อได้รับการยืนยันข้อบกพร่อง connect.microsoft.com/VisualStudio/feedback/details/779528/…หากต้องการติดตามแบบสาธารณะ
Kyle

43

ฉันคิดว่าคุณพบข้อบกพร่องแล้ว

สิ่งนี้ใช้งานได้ (แสดงข้อยกเว้นที่ยืนยัน):

var a = 1;
var b = 2;
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

และใช้งานได้ (ส่งออกข้อความ):

var a = new{c=1};
var b = new{c=2};
Console.WriteLine(string.Format("Not equal {0} {1}", a, b));

แต่ไม่ได้ผล (พ่น a FormatException):

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

ฉันคิดไม่ออกว่าจะเกิดพฤติกรรมแบบนี้ ฉันจะส่งรายงานข้อบกพร่อง ในระหว่างนี้นี่คือวิธีแก้ปัญหา:

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, string.Format("Not equal {0} {1}", a, b));

5

ฉันเห็นด้วยกับ @StriplingWarrior ว่าสิ่งนี้ดูเหมือนจะเป็นจุดบกพร่องของเมธอด Assert.AreEqual () อย่างน้อย 2 โอเวอร์โหลด ตามที่ StiplingWarrior ได้ชี้ให้เห็นแล้วสิ่งต่อไปนี้ล้มเหลว

var a = new { c = 1 };
var b = new { c = 2 };
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

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

// specify variable data type rather than "var"...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

และ

// specify variable data type and name the type on the generic overload of AreEqual()...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual<Size>(a, b, "Not equal {0} {1}", a, b);

สิ่งนี้ทำให้ฉันคิด System.Drawing.Size เป็นโครงสร้าง แล้ววัตถุล่ะ? รายการพารามิเตอร์ไม่ระบุว่ารายการหลังจากที่ข้อความเป็นstring params object[]ในทางเทคนิคใช่โครงสร้างเป็นวัตถุ ... แต่เป็นวัตถุชนิดพิเศษเช่นประเภทค่า ฉันคิดว่านี่คือจุดที่จุดบกพร่องอยู่ ถ้าเราใช้วัตถุของเราเองที่มีการใช้งานและโครงสร้างที่คล้ายกันSizeดังต่อไปนี้จริงไม่ทำงาน

private class MyClass
{
    public MyClass(int width, int height)
        : base()
    { Width = width; Height = height; }

    public int Width { get; set; }
    public int Height { get; set; }
}

[TestMethod]
public void TestMethod1()
{
    var test1 = new MyClass(0, 0);
    var test2 = new MyClass(1, 1);
    Assert.AreEqual(test1, test2, "Show me A [{0}] and B [{1}]", test1, test2);
}

1
ปัญหาไม่ได้อยู่ที่ว่าเป็นclassหรือstructแต่เป็นToStringค่าที่มีวงเล็บปีกกาที่มีลักษณะเป็นString.Format.
Jean Hominal

3

ฉันคิดว่าการยืนยันครั้งแรกไม่ถูกต้อง

ใช้สิ่งนี้แทน:

Assert.AreEqual(struct1, 
                struct2, 
                string.Format("Failed expected {0} actually is {1}", struct1, struct2));

ตามเอกสารฉันควรจะสามารถเรียก AreEqual ด้วยสตริงที่จัดรูปแบบได้ msdn.microsoft.com/en-us/library/ms243436%28v=vs.100%29.aspxเฉพาะพารามิเตอร์ Type: System.Object [] อาร์เรย์ของพารามิเตอร์ที่จะใช้เมื่อจัดรูปแบบข้อความ
Kyle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.