2013-04-23 102 views
4

我正在編寫針對我們核心框架的單元測試過程,並且遇到了這個問題。匿名類型的默認(T)行爲

我們有一個看起來像這樣的擴展方法:

public static T ThrowIfDefault<T>(this T self, string variableName) 
    { 
     if (self.Equals(default(T))) 
      throw new ArgumentException(string.Format("'{0}' cannot be default(T)", variableName)); 
     return self; 
    } // eo ThrowIfDefault<T> 

(上我看到這裏堆棧溢出ThrowIfNull<>擴展方法的變化

在此編寫測試用例,我第一次寫了一個幫手:

public void ThrowIfDefaultTestHelper<T>(T value) 
    { 
     // unit test *itself* requires that a value be specified!! 
     Assert.AreNotEqual(default(T), value); 

     // Good test 
     GenericExtensionMethods.ThrowIfDefault(value, "value"); 

     // Bad test 
     try 
     { 
      GenericExtensionMethods.ThrowIfDefault(default(T), "value"); 
     } 
     catch (ArgumentException) 
     { 
      // Expected result 
     } 
     catch (Exception) 
     { 
      throw; 
     } 
    } 

然後執行以下操作:

[TestMethod()] 
    public void ThrowIfDefaultTest() 
    { 
     ThrowIfDefaultTestHelper<int>(10); 
     ThrowIfDefaultTestHelper<Guid>(Guid.NewGuid()); 
     ThrowIfDefaultTestHelper<DateTime>(DateTime.Now); 
     ThrowIfDefaultTestHelper<object>(new { Name = "Test" }); // anonymous object 
    } 

由於我猜object沒有default(T)(或者是否?),所以單元測試在最後一個測試上失敗,因爲NullReferenceException被拋出。我能否以這種方式測試匿名對象?

+1

在一個方法中使用一個實例方法('Equals'),這種方法的唯一目的是檢查'null' ...... – 2013-04-23 18:51:06

+1

@DanielHilgarth,絕對是!當別人看的時候,人們錯過的小事就顯而易見了。因此,使用單元測試非常好主意......我們都是人類:) – 2013-04-23 18:54:02

回答

11

object確實有default(T),它恰好是null。這與使用非空值類型的其他測試用例不同。這就是爲什麼你得到一個NullReferenceException,而不是你所期望的。

如果用

EqualityComparer<T>.Default.Equals(obj, default(T)) 

更換

self.Equals(default(T)) 

你應該開始得到預期的ArgumentException

+0

*拍額頭*這非常有意義。謝謝。 – 2013-04-23 18:50:55

+0

此外,這是一個很好的例子,說明爲什麼單元測試對於明顯的「總是會工作」的代碼很重要:) – 2013-04-23 18:52:52

+0

最後一點,爲了清楚起見 - 我在那裏傳遞了'string',它不是一個值類型,而且測試通過的很好。我假設因此'default(T)'爲字符串是非空的? – 2013-04-23 18:57:04