Received.InOrder - using same instance in parameters

89 views
Skip to first unread message

Zsolt Soczo

unread,
Apr 26, 2015, 7:52:28 AM4/26/15
to nsubs...@googlegroups.com
I wanted to check that in a multi-step process the log is accurately reflects the state of the processing:


 Received.InOrder(
                () =>
                {
                    dal.UpdateLoaderJobStepLog(Arg.Is<LoaderJobStepLog>(
                        ljsl => ljsl.ExecutionStatus == JobExecutionStatus.Running
                                && ljsl.ExecutionStatusSp1 == JobExecutionStatus.FinishedSuccessfully
                                && ljsl.ExecutionStatusSp2 == JobExecutionStatus.Running
                                && ljsl.ExecutionStatusSp3 == JobExecutionStatus.NotStarted
                        ));
                    dal.UpdateLoaderJobStepLog(Arg.Is<LoaderJobStepLog>(
                        ljsl => ljsl.ExecutionStatus == JobExecutionStatus.Running
                                && ljsl.ExecutionStatusSp1 == JobExecutionStatus.FinishedSuccessfully
                                && ljsl.ExecutionStatusSp2 == JobExecutionStatus.FinishedSuccessfully
                                && ljsl.ExecutionStatusSp3 == JobExecutionStatus.Running
                        ));

...
);

This is not working as expected, because the class calling dal.UpdateLoaderJobStepLog modifies the passed LoaderJobStepLog at every step, and persists it using the dal instance. Naturally Received.InOrder checks args after all steps already happened, so it observes the final LoaderJobStepLog object which does not have the desired properties at that time, it reflects the last effect of its last modification. 

I know testing method call order is somewhat controversial in testing, but in this case it is in important part of the specification, so I felt i have to test it. 

When I clone the LoaderJobStepLog every time I pass it to the dal interface the test obviously becomes green, because all calls got a distinct object.

I suppose the correct way to test order in this case to use Arg.Do, because in this case I prepare asserts before running the target method, so I got callbacks for all steps.

The test using Arg.Do is a bit more verbose, but works:

var callOrder = 0;
dal.UpdateLoaderJobStepLog(Arg.Do<LoaderJobStepLog>(
    ljsl =>
    {
        callOrder++;

        if (ljsl.LoaderJobStepId == 0
            && ljsl.ExecutionStatus == JobExecutionStatus.Running
            && ljsl.ExecutionStatusSp1 == JobExecutionStatus.FinishedSuccessfully
            && ljsl.ExecutionStatusSp2 == JobExecutionStatus.Running
            && ljsl.ExecutionStatusSp3 == JobExecutionStatus.NotStarted)
        {
            Assert.AreEqual(1, callOrder);
            return;
        }

        if (ljsl.LoaderJobStepId == 0
            && ljsl.ExecutionStatus == JobExecutionStatus.Running
            && ljsl.ExecutionStatusSp1 == JobExecutionStatus.FinishedSuccessfully
            && ljsl.ExecutionStatusSp2 == JobExecutionStatus.FinishedSuccessfully
            && ljsl.ExecutionStatusSp3 == JobExecutionStatus.Running)
        {
            Assert.AreEqual(2, callOrder);
            return;
        }

Is this the preferred way to test method sequencing?

Thanks,
Zsolt





David Tchepak

unread,
Apr 26, 2015, 7:53:50 PM4/26/15
to nsubs...@googlegroups.com
Hi Zsolt,

Thank you for the very clearly explained question. 

I would try to separate the assertion from the "capturing" of the call details. (i.e. take the assertion out of the Arg.Do).
Something like this:

var calls = new List<Tuple<JobExecutionStatus, JobExecutionStatus, JobExecutionStatus, JobExecutionStatus>>();
dal.UpdateLoaderJobStepLog(Arg.Do<LoaderJobStepLog>(
    ljsl => calls.Add(Tuple.Create(ljsl.ExecutionStatus, ljsl.ExecutionStatus1, ljsl.ExecutionStatus2, ljsl.ExecutionStatus3))
    );

(You might want to use a custom type instead of a Tuple to make it look a bit nicer.)
We can then assert on the calls collection and make sure the calls were received in the correct order with the expected arguments.

I like this approach because it plays to strengths of different libraries: the mocking library for configuring some behaviour for a particular group of calls, and the assertion library for comparing the results/output of that behaviour.

Hope this helps. I'd be interested to hear how you go with it if you try it out.

Regards,
David







--
You received this message because you are subscribed to the Google Groups "NSubstitute" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nsubstitute...@googlegroups.com.
To post to this group, send email to nsubs...@googlegroups.com.
Visit this group at http://groups.google.com/group/nsubstitute.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages