Yapıları karşılaştırırken bu neden bir format istisnası atıyor?


94

İki System.Drawing.Sizeyapının eşitliğini ileri sürmeye çalışıyorum ve beklenen iddia hatası yerine bir format istisnası alıyorum.

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

Bu amaçlanan davranış mı? Burada yanlış bir şey mi yapıyorum?


Assert.AreEqual(struct1, struct2, string.Format("Failed expected {0} actually is {1}struct1.ToString (), struct2.ToString ())) `` sahip olmayı denediniz mi?
DiskJunky

Bu iyi çalışıyor; ancak Assert.AreEqual () 'ın yapı türleriyle bir dizeyi neden biçimlendiremediğini merak ediyorum.
Kyle

@Kyle Merak ettiğim için, bu Birim Test çerçevesinin Silverlight uyumlu sürümüyle değil, değil mi? Bu DLL'lerle yeniden üretebilirim (henüz tam .NET framework sürümünü denemedim) EDIT: boşver, tam olanlarla da test edildi ve hala başarısız oldu. :)
Chris Sinclair

@ChrisSinclair hayır, bu, Visual Studio 2010 ultimate ile gelen mstest'in herhangi bir sürümünü kullanıyor. Test projesi .NET Framework 4'ü hedefliyor
Kyle

4
Umursuyor musun emin değilim, ama bu NUnit'te iyi çalışıyor. MStest'te buna benzer daha fazla "sorun" gördüm. NUnit biraz daha olgun görünüyor (en azından bana göre). Yazı için + 1
BAS

Yanıtlar:


100

Ben hallederim. Ve evet, bu bir böcek.

Sorun şu ki string.Format, burada devam etmenin iki seviyesi var.

İlk biçimlendirme düzeyi gibi bir şey:

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

Ardından string.Format, sağladığınız parametrelerle birlikte kullanırız:

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

(Belli ki sağlanan kültürler ve bir çeşit temizlik var ... ama yeterli değil.)

Bu iyi görünüyor - beklenen ve gerçek değerlerin kendileri bir dizeye dönüştürüldükten sonra küme ayraçları ile bitmedikçe - bunun için yapıyorlar Size. Örneğin, ilk boyutunuz şu şekilde dönüştürülür:

{Width=0, Height=0}

Dolayısıyla, ikinci düzey biçimlendirme şuna benzer:

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

... ve başarısız olan da bu. Ah.

Aslında, biçimlendirmeyi parametrelerimizi beklenen ve gerçek parçalar için kullanacak şekilde kandırarak bunu gerçekten kolayca kanıtlayabiliriz:

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

Sonuç:

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

Açıkça kırıldı, çünkü beklemiyorduk foove gerçek değer değildi bar!

Temelde bu bir SQL enjeksiyon saldırısı gibidir, ancak daha az korkutucu bağlamda string.Format.

Geçici bir çözüm olarak, string.FormatStriplingWarrior'ın önerdiği gibi kullanabilirsiniz . Bu, gerçek / beklenen değerlerle biçimlendirme sonucunda ikinci düzey biçimlendirmenin gerçekleştirilmesini önler.


Ayrıntılı cevap için teşekkürler Jon! StriplingWarriors'ı kullanmaya başladım.
Kyle

1
%*nEşdeğeri yok mu? :(
Tom Hawtin - tackline

Bunun için bir hata raporu veren var mı?
Kevin

@Kevin: Evet - dahili olmasına rağmen, bu nedenle, ilerlemenin düzeltilene kadar kamuya açık olup olmayacağından emin değilim.
Jon Skeet

1
@Kevin Bir hata doğrulandıktan sonra MS'e de bir tane koydum. genel olarak izlemek istiyorsanız connect.microsoft.com/VisualStudio/feedback/details/779528/… .
Kyle

43

Sanırım bir hata buldunuz.

Bu çalışır (bir assert istisnası atar):

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

Ve bu işe yarar (mesajın çıktısını verir):

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

Ancak bu işe yaramıyor (a atıyor FormatException):

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

Bunun beklenen bir davranış olması için herhangi bir neden düşünemiyorum. Bir hata raporu gönderirdim. Bu arada, işte bir çözüm:

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

5

@StriplingWarrior'a katılıyorum, bunun gerçekten de Assert.AreEqual () yönteminde en az 2 aşırı yüklemede bir hata gibi göründüğüne katılıyorum. StiplingWarrior'un zaten işaret ettiği gibi, aşağıdakiler başarısız olur;

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

Kod kullanımında biraz daha açık olmak için bunun üzerinde biraz deney yapıyorum. Aşağıdakiler de çalışmaz;

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

Ve

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

Bu beni düşündürdü. System.Drawing.Size bir yapıdır. Peki ya nesneler? Parametre listesi , mesajdan sonraki listenin olduğunu belirtir . Teknik olarak, evet yapılar vardır objeler ... ama özel çeşit yani nesnelerin, değer türleri. Sanırım böceğin yattığı yer burası. Biz de benzer bir kullanım ve yapısı ile kendi nesne kullanırsanız , şu aslında yapar eser;stringparams 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
Sorun, classya da olması değil struct, ToStringdeğerin a gibi görünen küme parantezleri içerip içermediğidir String.Format.
Jean Hominal

3

İlk iddianın yanlış olduğunu düşünüyorum.

Bunun yerine şunu kullanın:

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

Belgelere göre, biçimlendirilmiş bir dizeyle AreEqual'ı çağırabilmeliyim. msdn.microsoft.com/en-us/library/ms243436%28v=vs.100%29.aspx , özellikle parametreler Tür: System.Object [] İletiyi biçimlendirirken kullanılacak bir dizi parametre.
Kyle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.