0

I have a service that

  1. Makes a call to each registered class that implements a specific interface (IUnorderedService)
  2. Then makes a final call to another service (IFinalService)

I want to write a test that asserts that my service makes a call to each IUnorderedService in no specific order, and then makes a call to the IFinalService. Using FakeItEasy, is there any way to do this?

My implementation looks like this (much simplified):

public class MyService
{
    private readonly IFinalService finalService;

    private readonly IEnumerable<IUnorderedService> unorderedServices;

    public MyService(
        IFinalService finalService,
        IEnumerable<IUnorderedService> unorderedServices)
    {
        this.finalService = finalService;
        this.unorderedServices = unorderedServices;
    }

    public void Execute()
    {
        foreach (var unorderedService in this.unorderedServices)
        {
            unorderedService.Execute();
        }

        this.finalService.Execute();
    }
}

And my test method would look something like this

public void ShouldCallUnorderedService_BeforeCallingFinalService()
{
    // Arrange
    ...

    // Act
    myService.Execute();

    // Assert
    foreach (var unorderedService in unorderedServices)
    {
        A.CallTo(() => unorderedService.Execute()).MustHaveHappenedOnceExactly();
    }

    // Should be asserted that this call occurs after all of the above calls have occurred.
    A.CallTo(() => finalService.Execute()).MustHaveHappenedOnceExactly();
}

I have played around with the A.CallTo().Then() syntax which works great for most purposes, but I don't see a way assert occurrence of multiple unordered calls prior to an ordered call.

dybzon
  • 1,236
  • 2
  • 15
  • 21

2 Answers2

1

You can put the A.CallTo().Then() into your foreach

    foreach (var unorderedService in unorderedServices)
    {
        A.CallTo(() => unorderedService.Execute()).MustHaveHappenedOnceExactly()
         .Then(A.CallTo(() => finalService.Execute()).MustHaveHappenedOnceExactly()
    }

So you are checking that each call to the unordered service happened before the call to the final service. Unfortunately this is not a bulk check, but I think the end result will still prove code correctness

Natalia Muray
  • 252
  • 1
  • 6
1

There's nothing directly built into FakeItEasy for this scenario. One option would be to intercept the calls as they are made. Then you could verify the order was as you wanted. The call-intercepting feature is (I believe) infrequently used, so this might be less understandable, but could be worth pursuing.

Then I thought about leveraging the "callback" or Invokes facility on a Fake to record whether a fake call was supposed to be final or not, then verify the setting at the end of the test:

[Fact]
public void ShouldCallUnorderedService_BeforeCallingFinalService()
{
    var lastKindOfService = "none";

    var unorderedServices = new [] {
        A.Fake<IUnorderedService>(),
        A.Fake<IUnorderedService>(),
        A.Fake<IUnorderedService>(),
    };

    foreach (var unorderedService in unorderedServices)
    {
        A.CallTo(() => unorderedService.Execute()).Invokes(() => lastKindOfService = "unordered");
    }

    var finalService = A.Fake<IFinalService>();
    A.CallTo(() => finalService.Execute()).Invokes(() => lastKindOfService = "final");

    var service = new MyService(finalService, unorderedServices);
    service.Execute();

    foreach (var unorderedService in unorderedServices)
    {
        A.CallTo(() => unorderedService.Execute()).MustHaveHappenedOnceExactly();
    }
    A.CallTo(() => finalService.Execute()).MustHaveHappenedOnceExactly();

    lastKindOfService.Should().Be("final");
}

Of course this could be prettied up with a helper method or enums for the state. And if there's any asynchrony, you might want to serialize the updates.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • 1
    Thanks for your thoughts, I like the idea. I have another slightly more complex scenario where there's another service that should be called before any of the unordered services. That should be possible to handle with an almost similar mechanism to what you suggest here. Anyway, those are probably all very niche/rare scenarios :) – dybzon Feb 11 '21 at 13:36