Почему это утверждение вызывает исключение формата при сравнении структур?


Я пытаюсь утверждать равенство двух System.Drawing.Size структуры, и я получаю исключение формата вместо ожидаемого сбоя assert.

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

это предполагаемое поведение? Я делаю что-то не так здесь?

4 95

4 ответа:

я понял. И да, это ошибка.

проблема в том, что есть два уровня string.Format идем дальше.

The первый уровень форматирования что-то вроде:

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. Это позволяет избежать второго уровня форматирования, выполняемого в результате форматирования с фактическими / ожидаемыми значениями.

Я думаю, что вы нашли ошибку.

это работает (выдает исключение assert):

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

но это не работает (бросает!--4-->):

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

Я согласен с @StriplingWarrior, что это действительно похоже на ошибку с утверждением.Метод 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);

это заставило меня задуматься. Система.Рисунок.Размер-это структура. Что о объекты? Список параметров тут укажите, что список после string сообщении params object[]. Технически, да структурирует are объекты...но особенный видов объектов, т. е., типы значений. Я думаю, что это, где ошибка кроется. Если мы используем наш собственный объект с аналогичным использованием и структурой 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);
}

Я думаю, что первое утверждение неверно.

используйте этот код:

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