구조를 비교할 때이 어설 션이 형식 예외를 던지는 이유는 무엇입니까?


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}) (struct2.ToString를 struct1.ToString, ()))`?
DiskJunky

잘 작동합니다. 그러나 Assert.AreEqual ()이 구조 유형으로 문자열을 형식화 할 수없는 이유가 궁금합니다.
Kyle

@Kyle 호기심에서 이것은 단위 테스트 프레임 워크의 Silverlight 호환 버전이 아닙니다. 해당 DLL로 재현 할 수 있습니다 (아직 전체 .NET 프레임 워크 버전을 시도하지 않았습니다) 편집 : 신경 쓰지 않고 전체 버전으로 테스트했지만 여전히 실패했습니다. :)
Chris Sinclair

@ChrisSinclair 아니요, 이것은 Visual Studio 2010 ultimate과 함께 제공되는 모든 버전의 mstest를 사용하고 있습니다. 테스트 프로젝트 자체는 .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-tackline

이것에 대한 버그 보고서를 제출 한 사람이 있습니까?
케빈

@Kevin : 네-내부적으로는 있지만 진행 상황이 수정 될 때까지 공개적으로 표시 될지 확실하지 않습니다.
Jon Skeet 2013

1
@Kevin 버그가 확인되면 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));

그러나 이것은 작동하지 않습니다 (을 던졌습니다 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에 동의합니다. 이것이 실제로 2 개 이상의 오버로드에서 Assert.AreEqual () 메서드의 버그 인 것처럼 보입니다. 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는 구조체입니다. 물체는 어떻습니까? param 목록 string 메시지 뒤의 목록 이 params object[]. 기술적으로 yes 구조체 객체이지만 특별한 종류 의 객체, 즉 값 유형입니다. 나는 이것이 버그가있는 곳이라고 생각한다. 와 유사한 사용법과 구조를 가진 자체 객체를 사용 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 2013 년

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 , 특히 매개 변수 형식 : System.Object [] 메시지 서식을 지정할 때 사용할 매개 변수 배열입니다.
Kyle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.