2015-01-03 94 views
7

我有一個幫助我的單元測試方法,它聲稱按特定順序提出了一個特定的事件序列。代碼如下:測試助手預期事件序列報告重複事件

public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction) 
{ 
    var expectedSequence = new Queue<int>(); 
    for (int i = 0; i < subscribeActions.Count; i++) 
    { 
     expectedSequence.Enqueue(i); 
    } 

    ExpectEventSequence(subscribeActions, triggerAction, expectedSequence); 
} 

    public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence) 
    { 
     var fired = new Queue<int>(); 
     var actionsCount = subscribeActions.Count; 

     for(var i =0; i< actionsCount;i++) 
     { 
      subscription((o, e) => 
       { 
        fired.Enqueue(i); 
       }); 
     } 

     triggerAction(); 

     var executionIndex = 0; 

     var inOrder = true; 

     foreach (var firedIndex in fired) 
     { 

      if (firedIndex != expectedSequence.Dequeue()) 
      { 
       inOrder = false; 
       break; 
      } 

      executionIndex++; 
     } 

     if (subscribeActions.Count != fired.Count) 
     { 
      Assert.Fail("Not all events were fired."); 
     } 

     if (!inOrder) 
     { 
      Assert.Fail(string.Format(
       CultureInfo.CurrentCulture, 
       "Events were not fired in the expected sequence from element {0}", 
       executionIndex)); 
     } 

    } 

用法示例如下:

[Test()] 
    public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel) 
    { 
     var fuelTank = new FuelTank() 
     { 
      MaxFuel = maxFuel 
     }; 

     var eventHandlerSequence = new Queue<Action<EventHandler>>(); 

     eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x); 

     //Dealing with a subclass of EventHandler 
     eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e)); 

     Test.ExpectEventSequence(eventHandlerSequence,() => fuelTank.FillFuel()); 
    } 

和被測代碼:

public float Fuel 
    { 
     get 
     { 
      return fuel; 
     } 
     private set 
     { 
      var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel)); 

      if (fuel != adjustedFuel) 
      { 
       var oldFuel = fuel; 

       fuel = adjustedFuel; 

       RaiseCheckFuelChangedEvents(oldFuel); 
      } 
     } 
    } 

    public void FillFuel() 
    { 
     Fuel = MaxFuel; 
    } 

    private void RaiseCheckFuelChangedEvents(float oldFuel) 
    { 
     FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel)); 

     if (fuel == 0) 
     { 
      FuelEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (fuel == MaxFuel) 
     { 
      FuelFull.FireEvent(this, EventArgs.Empty); 
     } 

     if (oldFuel == 0 && Fuel != 0) 
     { 
      FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (oldFuel == MaxFuel && Fuel != MaxFuel) 
     { 
      FuelNoLongerFull.FireEvent(this, EventArgs.Empty); 
     } 
    } 

所以測試預計FuelFilledFuelChanged之前被解僱,但實際上FuelChanged是第一個被解僱的,這個測試失敗了。

但是我的測試是報告FuelChanged被觸發兩次,但是當我單步執行代碼時,很顯然FuelFilledFuelChangedFuelChanged僅被觸發一次後被觸發。

我認爲這是事做lambda表達式與當地國家的工作方式,也許在for循環迭代變量被永遠只能設置爲最終值,所以我換成for循環與此:

 var subscriptions = subscribeActions.ToList(); 

     foreach (var subscription in subscriptions) 
     { 
      subscription((o, e) => 
       { 
        var index = subscriptions.IndexOf(subscription); 
        fired.Enqueue(index); 
       }); 
     } 

但是結果是一樣的,包含{1; 1}而不是{1; 0}。

現在我想知道是否將相同的lambda分配給兩個事件而不是使用不同的訂閱/索引狀態。有任何想法嗎?

更新:我無法獲得與任何答案至今發佈(與我最初的結果)的成功,儘管他們的相似之處,我實際的代碼,所以我相信這個問題在其他地方位於我FuelTank代碼。我已經貼了完整代碼FuelTank如下:

public class FuelTank 
{ 
    public FuelTank() 
    { 

    } 

    public FuelTank(float initialFuel, float maxFuel) 
    { 
     MaxFuel = maxFuel; 
     Fuel = initialFuel; 
    } 

    public float Fuel 
    { 
     get 
     { 
      return fuel; 
     } 
     private set 
     { 
      var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel)); 

      if (fuel != adjustedFuel) 
      { 
       var oldFuel = fuel; 

       fuel = adjustedFuel; 

       RaiseCheckFuelChangedEvents(oldFuel); 
      } 
     } 
    } 

    private float maxFuel; 

    public float MaxFuel 
    { 
     get 
     { 
      return maxFuel; 
     } 
     set 
     { 
      if (value < 0) 
      { 
       throw new ArgumentOutOfRangeException("MaxFuel", value, "Argument must be not be less than 0."); 
      } 
      maxFuel = value; 
     } 
    } 

    private float fuel; 

    public event EventHandler<FuelEventArgs> FuelChanged; 

    public event EventHandler FuelEmpty; 

    public event EventHandler FuelFull; 

    public event EventHandler FuelNoLongerEmpty; 

    public event EventHandler FuelNoLongerFull; 

    public void AddFuel(float fuel) 
    { 
     Fuel += fuel; 
    } 

    public void ClearFuel() 
    { 
     Fuel = 0; 
    } 

    public void DrainFuel(float fuel) 
    { 
     Fuel -= fuel; 
    } 

    public void FillFuel() 
    { 
     Fuel = MaxFuel; 
    } 

    private void RaiseCheckFuelChangedEvents(float oldFuel) 
    { 
     FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel)); 

     if (fuel == 0) 
     { 
      FuelEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (fuel == MaxFuel) 
     { 
      FuelFull.FireEvent(this, EventArgs.Empty); 
     } 

     if (oldFuel == 0 && Fuel != 0) 
     { 
      FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (oldFuel == MaxFuel && Fuel != MaxFuel) 
     { 
      FuelNoLongerFull.FireEvent(this, EventArgs.Empty); 
     } 
    } 
} 

FuelEventArgs看起來是這樣的:

public class FuelEventArgs : EventArgs 
{ 
    public float NewFuel 
    { 
     get; 
     private set; 
    } 

    public float OldFuel 
    { 
     get; 
     private set; 
    } 

    public FuelEventArgs(float oldFuel, float newFuel) 
    { 
     this.OldFuel = oldFuel; 
     this.NewFuel = newFuel; 
    } 
} 

FireEvent擴展方法是這樣的:

public static class EventHandlerExtensions 
{ 
    /// <summary> 
    /// Fires the event. This method is thread safe. 
    /// </summary> 
    /// <param name="handler"> The handler. </param> 
    /// <param name="sender"> Source of the event. </param> 
    /// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param> 
    public static void FireEvent(this EventHandler handler, object sender, EventArgs args) 
    { 
     var handlerCopy = handler; 

     if (handlerCopy != null) 
     { 
      handlerCopy(sender, args); 
     } 
    } 

    /// <summary> 
    /// Fires the event. This method is thread safe. 
    /// </summary> 
    /// <typeparam name="T"> The type of event args this handler has. </typeparam> 
    /// <param name="handler"> The handler. </param> 
    /// <param name="sender"> Source of the event. </param> 
    /// <param name="args"> The <see cref="EventArgs"/> instance containing the event data. </param> 
    public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs 
    { 
     var handlerCopy = handler; 

     if (handlerCopy != null) 
     { 
      handlerCopy(sender, args); 
     } 
    } 
} 

完整的測試代碼可以在問題的上面找到,在測試執行期間沒有其他代碼被調用。

我通過統一測試工具插件的Unity3D引擎使用NUnit測試框架,.NET版本3.5(ISH,它更接近單聲道2.0,我相信),和Visual Studio 2013年

更新2 :

將代碼和測試提取到他們自己的項目(Unity3D生態系統之外)後,所有測試都按預期運行,所以我將不得不將這一個填充到Unity - > Visual工作室橋樑。

+0

有趣的問題......我試着通過使用NUnit重現你的情況(我不確定這單元測試你實際使用的是儘管工具包),但我只拿到了FuelChanged事件的一個觸發(指數訂閱等於1),接着是FuelFull事件(訂閱索引等於0),然後是未通過測試的FuelNoLongerEmpty事件。 所以我必須在我的事件處理代碼中有不同的東西。 AFAIK,Lambda表達式按照定義進行懶惰評估,因此它們在實際調用之前不會被評估,而是編譯到與使用委託語法相同的東西。 – RvdV79

+0

這確實是NUnit,我很困惑你對我有不同的結果,我會看看我的代碼,看看我是否在將它複製到問題中時有任何錯誤。 –

+0

首先,我需要在公共靜態無效ExpectEventSequence的示例中更改原始代碼,因爲訂閱未知。因此我使用了你的最後一個循環。 除此之外,最好知道您的事件處理程序(或使用它的基礎)是什麼。我正在使用NUnit 2.6.4。如果可能的話,你可能會爲你的例子添加更多的代碼(而不會暴露給很多機密信息:)) – RvdV79

回答

4

我根據尼克的問題如下實施。

第一類爲FuelTank:

public class FuelTank 
{ 
    private float fuel; 

    //Basic classes for the event handling, could be done by providing a few simple delegates, 
    //but this is just to stick as close to the original question as possible. 
    public FuelChanged FuelChanged = new FuelChanged(); 
    public FuelEmpty FuelEmpty = new FuelEmpty(); 
    public FuelFull FuelFull = new FuelFull(); 
    public FuelNoLongerEmpty FuelNoLongerEmpty = new FuelNoLongerEmpty(); 
    public FuelNoLongerFull FuelNoLongerFull = new FuelNoLongerFull(); 


    public float MaxFuel { get; set; } 

    public float Fuel 
    { 
     get 
     { 
      return fuel; 
     } 
     private set 
     { 
      var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel)); 

      if (fuel != adjustedFuel) 
      { 
       var oldFuel = fuel; 

       fuel = adjustedFuel; 

       RaiseCheckFuelChangedEvents(oldFuel); 
      } 
     } 
    } 

    public void FillFuel() 
    { 
     Fuel = MaxFuel; 
    } 

    private void RaiseCheckFuelChangedEvents(float oldFuel) 
    { 
     FuelChanged.FireEvent(this, new FuelEventArgs(oldFuel, Fuel)); 

     if (fuel == 0) 
     { 
      FuelEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (fuel == MaxFuel) 
     { 
      FuelFull.FireEvent(this, EventArgs.Empty); 
     } 

     if (oldFuel == 0 && Fuel != 0) 
     { 
      FuelNoLongerEmpty.FireEvent(this, EventArgs.Empty); 
     } 
     else if (oldFuel == MaxFuel && Fuel != MaxFuel) 
     { 
      FuelNoLongerFull.FireEvent(this, EventArgs.Empty); 
     } 
    }  
} 

至於事件處理程序缺少的代碼,我做了一個假設,使用此。正如註釋在前面的代碼塊中所描述的,使用普通代表可以更輕鬆地完成此操作。這只是一個選擇的問題,爲此,我覺得這個實現是不是最好的呢,卻足以調試適合:

public class FuelEventArgs : EventArgs 
{ 
    private float oldFuel, newFuel; 

    public FuelEventArgs(float oldFuel, float newFuel) 
    { 
     this.oldFuel = oldFuel; 
     this.newFuel = newFuel; 
    } 
} 

public class FuelEvents 
{  
    public event EventHandler FireEventHandler; 

    public virtual void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     EventHandler handler = FireEventHandler; 
     if (null != handler) 
      handler(this, fuelArgs); 
    } 

} 

public class FuelChanged : FuelEvents 
{    

    public override void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     Console.WriteLine("Fired FuelChanged"); 
     base.FireEvent(sender, fuelArgs); 
    } 
} 

public class FuelEmpty : FuelEvents 
{ 
    public override void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     Console.WriteLine("Fired FuelEmpty"); 
     base.FireEvent(sender, fuelArgs); 
    } 
} 

public class FuelFull : FuelEvents 
{ 
    public override void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     Console.WriteLine("Fired FuelFull"); 
     base.FireEvent(sender, fuelArgs); 
    } 
} 

public class FuelNoLongerEmpty : FuelEvents 
{ 
    public override void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     Console.WriteLine("Fired FuelNoLongerEmpty"); 
     base.FireEvent(sender, fuelArgs); 
    } 
} 

public class FuelNoLongerFull : FuelEvents 
{ 
    public override void FireEvent(object sender, EventArgs fuelArgs) 
    { 
     Console.WriteLine("Fired FuelNoLongerFull"); 
     base.FireEvent(sender, fuelArgs); 
    } 
} 

,並測試了這一切,我用這個類,包含從原來的大多數代碼問題:

[TestFixture] 
public class Tests 
{ 
    public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction) 
    { 
     var expectedSequence = new Queue<int>(); 
     for (int i = 0; i < subscribeActions.Count; i++) 
     { 
      expectedSequence.Enqueue(i); 
     } 

     ExpectEventSequence(subscribeActions, triggerAction, expectedSequence); 
    } 

    public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence) 
    { 
     var fired = new Queue<int>(); 
     var actionsCount = subscribeActions.Count; 

     //This code has been commented out due to the fact that subscription is unknown here. 
     //I stuck to use the last solution that Nick provided himself 

     //for (var i = 0; i < actionsCount; i++) 
     //{ 
     // subscription((o, e) => 
     // { 
     //  fired.Enqueue(i); 
     // }); 
     //} 

     var subscriptions = subscribeActions.ToList(); 

     foreach (var subscription in subscriptions) 
     { 
      subscription((o, e) => 
      { 
       var index = subscriptions.IndexOf(subscription); 
       Console.WriteLine("[ExpectEventSequence] Found index: {0}", index); 
       fired.Enqueue(index); 
      }); 
     } 

     triggerAction(); 

     var executionIndex = 0; 

     var inOrder = true; 

     foreach (var firedIndex in fired) 
     { 

      if (firedIndex != expectedSequence.Dequeue()) 
      { 
       inOrder = false; 
       break; 
      } 

      executionIndex++; 
      Console.WriteLine("Execution index: {0}", executionIndex); 
     } 

     if (subscribeActions.Count != fired.Count) 
     { 
      Assert.Fail("Not all events were fired."); 
     } 

     if (!inOrder) 
     { 
      Console.WriteLine("Contents of Fired Queue: {0}", PrintValues(fired)); 

      Assert.Fail(string.Format(
       CultureInfo.CurrentCulture, 
       "Events were not fired in the expected sequence from element {0}", 
       executionIndex)); 

     } 
    } 

    private static string PrintValues(Queue<int> myCollection) 
    { 
     return string.Format("{{0}}", string.Join(",", myCollection.ToArray())); 

    } 


    [Test()] 
    [ExpectedException(typeof(DivideByZeroException))] 
    public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel) 
    { 

     var fuelTank = new FuelTank() 
     { 
      MaxFuel = maxFuel 
     }; 

     var eventHandlerSequence = new Queue<Action<EventHandler>>(); 

     eventHandlerSequence.Enqueue(x => fuelTank.FuelFull.FireEventHandler += x); 

     //Dealing with a subclass of EventHandler 
     eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged.FireEventHandler += (o, e) => x(o, e)); 

     ExpectEventSequence(eventHandlerSequence,() => fuelTank.FillFuel()); 
    } 
} 

現在,運行與NUnit的測試時,我發現了以下結果:

是得到了觸發是FuelChanged事件的第一個事件,這將導致該方法中的發射隊列

public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence) 

包含{1}。

觸發的下一個事件是FuelFull事件,這意味着該燒製隊列現在包含: {1,0}根據尼克的問題如預期。

觸發的最後一個事件是FuelNoLongEmpty事件,而且這個事件未通過測試。

注:
因爲這些代碼還沒有提供答案的事實原來的問題是拉姆達的可能會造成一定的干擾,正如我上面提供的代碼,不只是正確的事情。

以下規則適用於變量的作用域在lambda表達式:

  • 所拍攝的圖像會被垃圾收集,直到 委託引用它超出範圍的變量。
  • 在lambda表達式中引入的變量在外部方法中不可見
  • lambda表達式不能直接從封閉方法中捕獲ref或out參數
  • 在lambda表達式中的返回語句不會導致返回包含方法
  • 一個lambda表達式不能包含goto語句,break語句, 或continue語句,其目標位於包含的匿名函數的主體之外或正文 中。

因此,尼克原來問題中的問題可能是由於您列舉了一個隊列。枚舉並直接傳遞給lambda表達式時,您將使用引用。一個技巧可能是通過將其複製到迭代循環範圍內的局部變量來實際去引用它。這正是他在帖子中提到的smiech

編輯:

我只是看着它再次爲您服務。你確定你所面臨的'挑戰'不僅僅是將被解僱的字典的索引與期望的序列進行比較,而是以相反的順序進行。請注意,隊列是基於FIFO的,所以當出隊時,它將檢索插入的第一個...

我注意到(根據我的代碼)已激活的字典包含{1,0},而expectedSequence字典包含{0,1}。通過查看預期的事件,這對於期望的序列隊列是有利的。因此,實際上,通過事件處理程序的「年齡」錯誤地構建了被觸發的隊列(填充在最後的代碼塊中)。

當我改變你在原來的

public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence) 

方法從

var subscriptions = subscribeActions.ToList(); 

    foreach (var firedIndex in fired) 
    { 

    if (firedIndex != expectedSequence.Dequeue()) 
    { 
     inOrder = false; 
     break; 
    } 

    executionIndex++; 
    Console.WriteLine("Execution index: {0}", executionIndex); 
    } 

本提供的代碼中的一個聲明:

//When comparing indexes, you'll probably need to reverse the fired queue 
    fired = new Queue<int>(fired.Reverse()); 
    foreach (var firedIndex in fired) 
    { 

    if (firedIndex != expectedSequence.Dequeue()) 
    { 
     inOrder = false; 
     break; 
    } 

    executionIndex++; 
    Console.WriteLine("Execution index: {0}", executionIndex); 
    } 

那麼一切都在您的測試會通過完美無缺,如你可以看到這個截圖:

enter image description here

+0

對我來說,''''''指數包含{1,1}。雖然你是對的,但這是另一個我沒有預見到的錯誤,我將在代碼中使用'.Reverse()'。 –

+0

嗨@NickUdell,你碰巧有NUnit的輸出嗎? – RvdV79

+0

不幸的是,它似乎在Unity中的包裝中運行,並沒有給出太多的信息。我會嘗試從任何Unity庫中提取代碼並在自己的解決方案中運行它。 –

2

對於第一部分:是的,它不得不採用lambdas變量作用域的方式。請參閱Access to Modified Closure。 因爲我花了一些時間試圖弄清楚,我允許自己粘貼我用過的代碼(所有測試都通過了)。

class Test 
{ 
    public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction) 
    { 
     var expectedSequence = new Queue<int>(); 
     for (int i = 0; i < subscribeActions.Count; i++) 
      expectedSequence.Enqueue(i); 
     ExpectEventSequence(subscribeActions, triggerAction, expectedSequence); 
    } 

    public static void ExpectEventSequence(Queue<Action<EventHandler>> subscribeActions, Action triggerAction, Queue<int> expectedSequence) 
    { 
     var fired = new Queue<int>(); 
     var subscriptions = subscribeActions.ToList(); 

     foreach (var subscription in subscriptions) 
     { 
      subscription((o, e) => 
      { 
       var index = subscriptions.IndexOf(subscription); 
       fired.Enqueue(index); 
      }); 
     } 
     triggerAction(); 
     var executionIndex = 0; 
     var inOrder = true; 
     foreach (var firedIndex in fired) 
     { 
      if (firedIndex != expectedSequence.Dequeue()) 
      { 
       inOrder = false; 
       break; 
      } 
      executionIndex++; 
     } 
     if (subscribeActions.Count != fired.Count) 
      Assert.Fail("Not all events were fired."); 
     if (!inOrder) 
      Assert 
       .Fail(string.Format(
       CultureInfo.CurrentCulture, 
       "Events were not fired in the expected sequence from element {0}", 
       executionIndex)); 
    } 
} 

public class Fueled 
{ 
    public event EventHandler<FuelEventArgs> FuelChanged = delegate { }; 
    public event EventHandler FuelEmpty = delegate { }; 
    public event EventHandler FuelFull = delegate { }; 
    public event EventHandler FuelNoLongerFull = delegate { }; 
    public event EventHandler FuelNoLongerEmpty = delegate { }; 
    private float fuel; 

    public float Fuel 
    { 
     get{ return fuel; } 
     private set 
     { 
      var adjustedFuel = Math.Max(0, Math.Min(value, MaxFuel)); 

      if (fuel != adjustedFuel) 
      { 
       var oldFuel = fuel; 
       fuel = adjustedFuel; 
       RaiseCheckFuelChangedEvents(oldFuel); 
      } 
     } 
    } 

    public void FillFuel() 
    { 
     Fuel = MaxFuel; 
    } 

    public float MaxFuel { get; set; } 

    private void RaiseCheckFuelChangedEvents(float oldFuel) 
    { 
     FuelChanged(this, new FuelEventArgs(oldFuel, Fuel)); 

     if (fuel == 0) 
      FuelEmpty(this, EventArgs.Empty); 
     else if (fuel == MaxFuel) 
      FuelFull(this, EventArgs.Empty); 
     if (oldFuel == 0 && Fuel != 0) 
      FuelNoLongerEmpty(this, EventArgs.Empty); 
     else if (oldFuel == MaxFuel && Fuel != MaxFuel) 
      FuelNoLongerFull(this, EventArgs.Empty); 
    } 
} 

public class FuelEventArgs : EventArgs 
{ 
    public FuelEventArgs(float oldFuel, float fuel) 
    { 
    } 
} 

[TestFixture] 
public class Tests 
{ 
    [Test()] 
    public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel) 
    { 
     var fuelTank = new Fueled() 
     { 
      MaxFuel = maxFuel 
     }; 
     var eventHandlerSequence = new Queue<Action<EventHandler>>(); 
     //Dealing with a subclass of EventHandler 
     eventHandlerSequence.Enqueue(x => fuelTank.FuelChanged += (o, e) => x(o, e)); 
     eventHandlerSequence.Enqueue(x => fuelTank.FuelFull += x); 
     Test.ExpectEventSequence(eventHandlerSequence,() => fuelTank.FillFuel()); 
    } 
} 

基本上我只是改變了測試方法中預期事件的順序。如果您在更改循環後仍然得到不正確的結果,我認爲問題必須超出您的粘貼代碼範圍。我使用VS 2013社區+ ReSharper的8,NUnit的2.6.4.14350

編輯:不同的方法

我是想解決你實際上張貼的問題,但也許這實際上是你想要什麼: 你會不會考慮嘗試你的方法的簡化版本?:

[Test()] 
public void FillFuel_Test([Values(1, 5, 10, 100)]float maxFuel) 
{ 
    var fuelTank = new Fueled() 
    { 
     MaxFuel = maxFuel 
    }; 
    var expectedEventSequence = new[] 
    { 
     "FuelChanged", 
     "FuelFull" 
    }; 
    var triggeredEventSequence = new List<string>(); 
    fuelTank.FuelChanged += (o, e) => triggeredEventSequence.Add("FuelChanged"); 
    fuelTank.FuelFull += (o, e) => triggeredEventSequence.Add("FuelFull"); 

    fuelTank.FillFuel(); 

    Assert.AreEqual(expectedEventSequence,triggeredEventSequence); 
}