1

I created an extension method for calling moq in order (see below) per this SO answer.

An example use case is:

mock.setup.CallbackSequence(new List{h,e,l,l,o})

where I pass hello into my SUT and expect the characters in order.

The problem is this case:

mock.setup.CallbackSequence(new List{h,e,l,l,o,X})

where I pass hello. This will pass even though it should fail because of the dynamic assertion nature (X is never sent in, but we never know). The only way I can think of to fix this would be to add a

mock.Verify(setupmethod, Times.Exactly(list.Count))

However, the callback setup and the verify are done in two different places, so it would fall on the consumer of the extension method to realize this flaw. I would like to avoid putting that on them, as it is bound to fail.....suggestions?

public static ICallbackResult CallbackSequence<TMockType, T1, T2>(this ISetup<TMockType> mockSetup, IList<T1> sequencedList) where TMockType : class
{
  //Refactor: Does not fail if it does not reach the end, only if out of order and/or too long
  var index = 0;
  return mockSetup.Callback((T1 t1, T2 t2) =>
  {
    if(index >= sequencedList.Count)
      Assert.That(false, "Sequenced list attempted to access index out of bounds. Count is " + sequencedList.Count + "; index is " + index + "; value being checked is " + t1);
    var currentItemInSequence = sequencedList[index];
    Assert.That(t1, Is.EqualTo(currentItemInSequence), String.Format("Failed sequence at position{0}", index));
    index++;
  });
}

EDIT

The only thing I can think of that might work is to return a custom object that the consumer is supposed to call at the end:

var sequenceVerifier = ...CallbackSequence();
//Do Stuff
sequenceVerifier.VerifySequence();

This is still not optimal to me as this gives the impression that this will not verify until the VerifySequence is called, but it will only perform the edge case....unless I don't assert in the callback, but just track and assert at the end??????? That might work, thoughts????

Community
  • 1
  • 1
Justin Pihony
  • 66,056
  • 18
  • 147
  • 180

1 Answers1

1

After talking with some co-workers, we came up with a dual approach that will leave it up to the implementer.

Option 1

Return a SequenceVerifier that must have its VerifySequenceCalls method called

Option 2

Pass my action into a helper method so that it can setup, act, and then verify the edge case

I can use a SequenceVerifier in my 2nd option. Then, it is up to the consumer how they want their code to look.

public static MockSequenceVerifier<T1> CallbackInOrderOfSequence<TMockType, T1, T2>(this ISetup<TMockType> mockSetup, IList<T1> sequencedList) where TMockType : class
{
  var mockSequenceVerifier = new MockSequenceVerifier<T1>(sequencedList);
  mockSetup.Callback((T1 t1, T2 t2) => mockSequenceVerifier.UpdateSequence(t1));
  return mockSequenceVerifier;
}

public static void VerifySequenceAfterActionPerformed<TMockType, T1, T2>(this ISetup<TMockType> mockSetup, IList<T1> sequencedList, Action actionToPerform) where TMockType : class
{
  var verifier = CallbackInOrderOfSequence<TMockType, T1, T2>(mockSetup, sequencedList);
  actionToPerform();
  verifier.VerifySequence();
}

public class MockSequenceVerifier<T>
{
  private IList<T> SequenceToVerifyAgainst { get; set; } 
  private IList<T> ActualSequence { get; set; } 

  public MockSequenceVerifier(IList<T> sequenceToVerifyAgainst)
  {
    SequenceToVerifyAgainst = sequenceToVerifyAgainst;
    ActualSequence = new List<T>();
  } 

  public void VerifySequence()
  {
    ActualSequence.IsEqualTo(SequenceToVerifyAgainst);
  }

  public void UpdateSequence(T item)
  {
    ActualSequence.Add(item);
  }
}
Justin Pihony
  • 66,056
  • 18
  • 147
  • 180