特定の順序で特定のイベントシーケンスが発生したことを主張する私のユニットテストのヘルパーメソッドがあります。コードは以下の通りである:次のように予期したイベントのテストヘルパーシーケンスの重複イベントの報告
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);
}
}
だから、テストはFuelFilled
がFuelChanged
前に解雇されることを想定していたが実際にはFuelChanged
が最初に起動され、テストに失敗します。
しかし私のテストではなくFuelChanged
は二回発射されていることを報告しているが、私はコードをステップ実行するとき、FuelChanged
とFuelChanged
は1回のみ起動された後FuelFilled
が発射されることは明らかです。
私はラムダは多分ループイテレータ変数がしかの最終値に設定するので、私はこれでループのために置き換えられたため、地元の状態で作業道とは何かだったと仮定:
var subscriptions = subscribeActions.ToList();
foreach (var subscription in subscriptions)
{
subscription((o, e) =>
{
var index = subscriptions.IndexOf(subscription);
fired.Enqueue(index);
});
}
結果は同じですが、{1; 0}の代わりに{1; 1}が含まれています。
今私は、異なるサブスクリプション/インデックス状態を使用する代わりに、両方のイベントに同じラムダが割り当てられているのだろうかと思います。何か案は?
更新
:は、私は私の実際のコードへの類似性にもかかわらず、(私の最初の結果と同じ)これまでに掲載の答えのいずれかで成功を得ることができなかったので、私は問題が私の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 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);
}
}
}
完全なテストコード:
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
拡張メソッドがあるが、このようになりますテストの実行中に呼び出される他のコードはありません。私はUnity3Dエンジンのユニティテストツールのプラグインを介して、NUnitのテストフレームワークを使用しています
、.NETバージョン3.5(ISHは、それは私は信じて、モノラル2.0に近いです)、およびVisual Studio 2013
アップデート2 : - >ビジュアル期待通りにすべてのテストを実行し、自分のプロジェクト(Unity3Dの生態系の外側)に、コードやテストを抽出するので、私はユニティのバグまで、この1チョークする必要がありますするつもりだ後
スタジオブリッジ。
興味深い質問...私は(私はあなたが実際にかかわらず、使用しているunittestのツールキットわからないよ)NUnitのを使って、自分の状況を再現してみましたが、私は唯一のFuelChangedイベント(インデックスの1つのトリガーを得ましたサブスクリプションは1に等しい)、続いてFuelFullイベント(サブスクリプションのインデックスは0に等しい)、そしてテストに失敗したFuelNoLongerEmptyイベントが続きます。 私はイベントハンドラーコードで何か違うものを持っている必要があります。 AFAIK、ラムダ式は定義上遅延評価されるため、実際に呼び出されるまで評価されず、デリゲート構文を使用するのと同じものにコンパイルされます。 – RvdV79
それは本当にNUnitです、私はあなたが私に違う結果を持っているのは混乱しています。私は自分のコードを見て、それを質問にコピーする際に間違いがないかどうかを見ていきます。 –
まず、サブスクリプションが不明であったため、パブリックstatic void ExpectEventSequenceの例の中で元のコードを変更する必要がありました。私はあなたの最後のforループを使用しました。 それ以外にも、イベントハンドラ(またはそれを使用するときのベース)が何であるかを知ることは良いことです。私はNUnit 2.6.4を使用しています。可能であれば、あなたの例に少し多くのコードを追加するかもしれません(多くの機密情報を明らかにせずに) – RvdV79