Próbuję zapewnić równość dwóch System.Drawing.Size
struktur i otrzymuję wyjątek formatu zamiast oczekiwanego niepowodzenia potwierdzenia.
[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);
}
Czy to zamierzone zachowanie? Czy ja tu robię coś złego?
Assert.AreEqual(struct1, struct2, string.Format("Failed expected {0} actually is {1}
struct1.ToString (), struct2.ToString ())) `?Odpowiedzi:
Mam to. I tak, to błąd.
Problem w tym, że
string.Format
dzieje się tutaj na dwóch poziomach .Pierwszy poziom formatowania jest coś takiego:
string template = string.Format("Expected: {0}; Actual: {1}; Message: {2}", expected, actual, message);
Następnie używamy
string.Format
z podanymi parametrami:string finalMessage = string.Format(template, parameters);
(Oczywiście zapewniane są kultury i jakiś rodzaj sanityzacji ... ale to za mało).
Wygląda to dobrze - chyba że oczekiwane i rzeczywiste wartości same kończą się nawiasami klamrowymi po konwersji na ciąg - co robią
Size
. Na przykład Twój pierwszy rozmiar kończy się konwersją na:{Width=0, Height=0}
Więc drugi poziom formatowania to coś takiego:
string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " + "Message = Failed expected {0} actually is {1}", struct1, struct2);
... i to właśnie zawodzi. Auć.
Rzeczywiście, możemy to naprawdę łatwo udowodnić, oszukując formatowanie, aby użyć naszych parametrów dla oczekiwanych i rzeczywistych części:
var x = "{0}"; var y = "{1}"; Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar");
Wynik to:
Wyraźnie zepsuty, czego się nie spodziewaliśmy,
foo
ani rzeczywista wartośćbar
!Zasadniczo przypomina to atak polegający na wstrzyknięciu kodu SQL, ale w raczej mniej przerażającym kontekście
string.Format
.Aby obejść ten problem, możesz użyć tego,
string.Format
co sugeruje StriplingWarrior. Pozwala to uniknąć drugiego poziomu formatowania wykonywanego na wyniku formatowania z wartościami rzeczywistymi / oczekiwanymi.źródło
%*n
odpowiednika? :(Myślę, że znalazłeś błąd.
To działa (zgłasza wyjątek assert):
var a = 1; var b = 2; Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);
I to działa (wyświetla komunikat):
var a = new{c=1}; var b = new{c=2}; Console.WriteLine(string.Format("Not equal {0} {1}", a, b));
Ale to nie działa (rzuca a
FormatException
):var a = new{c=1}; var b = new{c=2}; Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);
Nie mogę wymyślić żadnego powodu, dla którego byłoby to oczekiwane zachowanie. Złożyłbym raport o błędzie. Tymczasem oto obejście:
var a = new{c=1}; var b = new{c=2}; Assert.AreEqual(a, b, string.Format("Not equal {0} {1}", a, b));
źródło
Zgadzam się z @StriplingWarrior, że to rzeczywiście wygląda na błąd w metodzie Assert.AreEqual () przy co najmniej 2 przeciążeniach. Jak już zauważył StiplingWarrior, następujące działania zawodzą;
var a = new { c = 1 }; var b = new { c = 2 }; Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);
Zrobiłem trochę eksperymentów w tym zakresie, aby być bardziej wyraźnym w użyciu kodu. Poniższe również nie działa;
// 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);
I
// 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);
To zmusiło mnie do myślenia. System.Drawing.Size to struktura. A co z przedmiotami? Lista param ma określić, że lista po
string
wiadomości wynosiparams object[]
. Technicznie rzecz biorąc, struktury tak są obiektami ... ale specjalnymi rodzajami obiektów, tj. Typami wartości. Myślę, że w tym tkwi błąd. Jeśli używamy nasz własny obiekt o podobnej strukturze do użytkowania iSize
dodaje się faktycznie robi praca;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); }
źródło
class
czystruct
, ale czyToString
wartość zawiera nawiasów klamrowych, które wyglądają jakString.Format
.Myślę, że pierwsze twierdzenie jest błędne.
Użyj tego zamiast tego:
Assert.AreEqual(struct1, struct2, string.Format("Failed expected {0} actually is {1}", struct1, struct2));
źródło