2015年5月16日

Assert.AreEqual に NaN が入るとテストが成功するバグ (VS2010~2012)

Visual Studio 2010 (たぶん 2012 も) の MSTest の delta を指定する Assert.AreEqual には下記のようなバグがある。

// delta を指定する場合
Assert.AreEqual(1.0, double.NaN, 1.0); // 成功

// delta を指定しない場合
Assert.AreEqual(1.0, double.NaN); // 失敗

// 失敗するのが正しい動作なので、delta を指定する方はバグっている

バグの原因はソースを見れば明らかで、NaN を考慮していないため。
[Assert.AreEqual のソース (ILSpy)]

public static void AreEqual(double expected, double actual, double delta, string message, params object[] parameters)
{
    if (Math.Abs(expected - actual) > delta)
    {
        string message2 = FrameworkMessages.AreEqualDeltaFailMsg((message == null) ? string.Empty : Assert.ReplaceNulls(message), expected.ToString(CultureInfo.CurrentCulture.NumberFormat), actual.ToString(CultureInfo.CurrentCulture.NumberFormat), delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
        Assert.HandleFail("Assert.AreEqual", message2, parameters);
    }
}

// 比較条件に NaN が含まれると false になるので if の中に入らない

Assert.AreEqual が含まれるアセンブリの配置場所は
%VS のディレクトリ%\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll

Visual Studio 2013 では修正されているが・・・

このバグは VS2013 では修正されている。
[修正済み Assert.AreEqual のソース (ILSpy)]

public static void AreEqual(double expected, double actual, double delta, string message, params object[] parameters)
{
    if (double.IsNaN(expected) || double.IsNaN(actual) || double.IsNaN(delta))
    {
        string message2 = FrameworkMessages.AreEqualDeltaFailMsg((message == null) ? string.Empty : Assert.ReplaceNulls(message), expected.ToString(CultureInfo.CurrentCulture.NumberFormat), actual.ToString(CultureInfo.CurrentCulture.NumberFormat), delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
         Assert.HandleFail("Assert.AreEqual", message2, parameters);
    }
    if (Math.Abs(expected - actual) > delta)
    {
        string message3 = FrameworkMessages.AreEqualDeltaFailMsg((message == null) ? string.Empty : Assert.ReplaceNulls(message), expected.ToString(CultureInfo.CurrentCulture.NumberFormat), actual.ToString(CultureInfo.CurrentCulture.NumberFormat), delta.ToString(CultureInfo.CurrentCulture.NumberFormat));
        Assert.HandleFail("Assert.AreEqual", message3, parameters);
    }
}

// 引数のいずれかが NaN の場合、テスト失敗

が、VS2010 をインストールしている場合、修正されたアセンブリ「Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll」(以降、UnitTestFramework.dll)が使われないケースがある。
[確認できた発生条件]
  • VS2010 と VS2013 インストール済 (インストール順序は関係ない)
  • .NET 4.0 以上のテストプロジェクト

原因

VS2010 は GAC に .NET 2.0 と 4.0 の UnitTestFramework.dll (バグ有り) をインストールするが、VS2013 は .NET 2.0 の UnitTestFramework.dll (修正済み) しかインストールしないため。
かつ、VS2010 の UnitTestFramework.dll (.NET 4.0) と VS2013 の UnitTestFramework.dll (.NET 2.0) のバージョン番号「10.0.0.0」が同じであるため。(公開キーは MS 製品なので同じ)
※他にバージョン番号「10.1.0.0」の UnitTestFramework.dll も存在するが、設定で参照されないようになっている

VS2010 と VS2013 をインストールした環境の GAC は下記の状態になる。
CLR 2.0 %windir%\assembly 修正済み
CLR 4 %windir%\Microsoft.NET\assembly バグ有り

そして、CLR 4 の実行アプリは CLR 4 の GAC を優先するため、バグ有りの方の UnitTestFramework.dll が参照されることになる。(参考:GAC 内のアセンブリ検索方法
公開キーが一緒なのは仕方ないとして、バージョン番号さえ変えとけば、こんな事態は回避できた・・・

解決手段

上記リンクでも説明されてるけど、CLR 4 の GAC の実ファイルを移動または削除すればOK。
%windir%\Microsoft.NET\assembly\GAC_MSIL\Microsoft.VisualStudio.QualityTools.UnitTestFramework\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll

副次的な効果として、VS2010 のテストプロジェクトからも修正された UnitTestFramework.dll が使用されるようになる。

0 件のコメント:

コメントを投稿