Blog.

Pitfall in FakeItEasy

MF

Marco Franssen /

4 min read760 words

Cover Image for Pitfall in FakeItEasy

The current project I'm doing is a CQRS based project, using the ncqrs framework. Today I came to a phase I needed some mocking done for some of my domain services. I choose FakeItEasy to handle this for me. So I started by just adding a reference to my DomainScenario project using a Nuget package.

For some of my complexer scenario's I ran into some issues which took me about about 45 minutes to fix one of my tests. For fixing all other failing tests I had to do the same trick.

Before I explain the issue I ran into with FakeItEasy, I have to mention I use my aggregate roots to prepare my eventstore. My issue is related to this specific way of preparing the eventstore.

For example I have a Participant aggregate root like below.

public class Participant : BisAggregateRoot
{
    private readonly IUniqueParticipantNrGuard _uniqueFormattedParticipantNrVerifier = NcqrsEnvironment.Get<IUniqueParticipantNrGuard>();

    //other members left for convenience
    private string _participantNrFormat;
    private int _participantNr;
    private Gender _gender;
    private PartialDate _birthDate;
    private string _remark;

    public Guid ParticipantId { get { return EventSourceId; } }

    // ReSharper disable UnusedMember.Local
    private Participant()
    // ReSharper restore UnusedMember.Local
    {
    }

    public Participant(Guid participantId, ScientificStudy scientificStudy, string participantNrFormat, int participantNr, Gender gender, PartialDate birthdate)
        : base(participantId)
    {
        _uniqueFormattedParticipantNrVerifier.GuardIsUnique(scientificStudy.ScientificStudyId, participantId,
                                                            string.Format(participantNrFormat, participantNr));
        ApplyEvent(new ParticipantAddedEvent(Credentials)
                       {
                           ParticipantId = participantId,
                           ParticipantNrFormat = participantNrFormat,
                           ParticipantNr = participantNr,
                           ScientificStudyId = scientificStudy.ScientificStudyId,
                           Gender = gender.ActualValue,
                           Birthdate = birthdate.ActualValue
                       });
    }

    public void Edit(ScientificStudy scientificStudy, string participantNrFormat, int participantNr, Gender gender, PartialDate birthdate, string remark)
    {
        //Some other business rules
        _uniqueFormattedParticipantNrVerifier.GuardIsUnique(scientificStudy.ScientificStudyId, ParticipantId,
                                                            string.Format(participantNrFormat, participantNr));
        //Some other business rules

        ApplyEvent(new ParticipantEditedEvent(Credentials)
            {
                ParticipantId = ParticipantId,
                ParticipantNrFormat = participantNrFormat,
                ParticipantNr = participantNr,
                Gender = gender.ActualValue,
                Birthdate = birthdate.ActualValue
            });
    }

    protected void OnParticipantAdded(ParticipantAddedEvent e)
    {
        _claimedAliquots = new List<ClaimedAliquot>();
        _participantNrFormat = e.ParticipantNrFormat;
        _participantNr = e.ParticipantNr;
        _gender = new Gender(e.Gender);
        _birthDate = new PartialDate(e.Birthdate);
    }

    protected void OnParticipantEdited(ParticipantEditedEvent e)
    {
        _participantNrFormat = e.ParticipantNrFormat;
        _participantNr = e.ParticipantNr;
        _gender = new Gender(e.Gender);
        _birthDate = new PartialDate(e.Birthdate);
        _remark = e.Remark;
    }
}

As you can see both the edit and the construction of this aggregate use the same domain service to validate if the participant number is unique. Keep this in mind because of my eventstore preparation style, because this relates to the problem.

For my eventstore I use some fluent builder thing I built, which uses the domainobjects to generate the actual events.

public static class New
{
    public static ScientificStudy ScientificStudy(string nr = "M001", string name = "Maastricht Study", string participantNrFormat = "P{0}", string boxNameFormat = "B{0}")
    {
        return new ScientificStudy(Default.Id.For.ScientificStudy, nr, name, participantNrFormat, boxNameFormat);
    }

    public static Participant Participant(ScientificStudy scientificStudy, string participantNrFormat = "P{0}", int participantNr = 1, int gender = 0, string birthdate = "19861117")
    {
        var realGender = new Gender(gender);
        var realBirthDate = new PartialDate(birthdate);
        return new Participant(Default.Id.For.Participant, scientificStudy, participantNrFormat, participantNr, realGender, realBirthDate);
    }

    //other members left for convenience
}

This builder is used like below in my tests. Notice the GetChanges method is a extension method on my aggregateRoots.

protected override void SetupDependencies()
{
    base.SetupDependencies();

    var eventStore = NcqrsEnvironment.Get<IEventStore>();
    var scientificStudy = New.ScientificStudy();
    var scientificStudyEvent = scientificStudy.TrackChanges().GetChanges();
    var participantEvents = New.Participant(scientificStudy).GetChanges();
    eventStore.Store(Prepare.Events(scientificStudyEvent).ForSourceUncomitted(Default.Id.For.ScientificStudy, Default.Id.Random()));
    eventStore.Store(Prepare.Events(participantEvents).ForSourceUncomitted(Default.Id.For.Participant, Default.Id.Random()));
}

For the test I want to do I registered my Fakes in first instance like below, which made my test fail. I am trying to test if the editing causes an 'FormattedParticipantNrShouldBeUniqueException' when changing the participantNr into an already excisting participantNr.

protected override void RegisterFakesInConfiguration(EnvironmentConfigurationWrapper configuration)
{
    var commandService = new CommandService();
    commandService.RegisterExecutor(new EditParticipantCommandExecutor());

    var uniqueParticipantGuard = A.Fake<IUniqueParticipantNrGuard>();
    A.CallTo(uniqueParticipantGuard).DoesNothing().Once();
    A.CallTo(uniqueParticipantGuard).Throws(
        new FormattedParticipantNrShouldBeUniqueException(_formattedParticipantNr));

    configuration.Register<ICommandService>(commandService);
    configuration.Register(uniqueParticipantGuard);

    //other fakes left for convenience

    base.RegisterFakesInConfiguration(configuration);
}

After 45 minutes searching and discussing with my colleague, we found a solution.

protected override void RegisterFakesInConfiguration(EnvironmentConfigurationWrapper configuration)
{
    var commandService = new CommandService();
    commandService.RegisterExecutor(new EditParticipantCommandExecutor());

    var uniqueParticipantGuard = A.Fake<IUniqueParticipantNrGuard>();
    A.CallTo(uniqueParticipantGuard).Throws(
        new FormattedParticipantNrShouldBeUniqueException(_formattedParticipantNr));
    A.CallTo(uniqueParticipantGuard).DoesNothing().Once();

    configuration.Register<ICommandService>(commandService);
    configuration.Register(uniqueParticipantGuard);

    //other fakes left for convenience

    base.RegisterFakesInConfiguration(configuration);
}

As you can see the solution was really simple, but hard to find, because debugging didn't highlighted this was the issue.

Just switch the order of configuring the Mocked/Faked object.

Last but not least I show you my actual test method.

[Then]
public void it_should_throw_an_formatted_participant_nr_should_be_unique_exception_containing_the_formatted_participant_nr()
{
    var exception = (FormattedParticipantNrShouldBeUniqueException)CaughtException;
    exception.FormattedParticipantNr.Should().Be(_formattedParticipantNr);
}

I'm not sure if I was just doing it wrong or if FakeItEasy just needs a fix to prevent you running into this issue. Hope I made the issue clear and it will save you some time when encountering some same issues.

You have disabled cookies. To leave me a comment please allow cookies at functionality level.

More Stories

Cover Image for Install Win 8 developer preview in your VirtualBox

Install Win 8 developer preview in your VirtualBox

MF

Marco Franssen /

Since today you can download the pre-release of Windows 8 (Developer preview). Since testing this new stuff out can be risky, it is best you do it in a virtual machine. You can download it the developer preview from the new Windows Dev Center. MSDN subscribers can download some additional win8 stuff. Before you start you have to make sure your pc supports hardware virtualization. Here you can find how to enable it in the BIOS if your system supports it. https://www.microsoft.com/windows/virtual…

Cover Image for Knockout that cascading dropdown

Knockout that cascading dropdown

MF

Marco Franssen /

In this article I will explain how you can make cascading dropdowns with Knockout.js. Knockout.js is a JavaScript library which provides you some stuff to implement the MVVM pattern.  Knockout provides you the following stuff: Declarative bindings: (Easily associate DOM elements with model data using a concise, readable syntax); Automatic UI Refresh: (When your data model's state changes, your UI updates automatically) Dependency tracking: (Implicitly set up chains of relationships between mo…

Cover Image for Lessons learned when applying DDD CQRS

Lessons learned when applying DDD CQRS

MF

Marco Franssen /

While I was trying to apply DDD in combination with CQRS I learned myself some good lessons. I was trying to apply this in an agile approach, using the project method SCRUM. I started the project by translating all use-cases to smaller user stories. Based on this user stories I defined a first sprint. In the first sprint I didn't had or make a design for the complete domain. So I wasn't completely sure if I made the right decision for the 'aggregate root / bounded contexts' needed for the firs…

Cover Image for Injecting dependencies in MVC3 with Windsor

Injecting dependencies in MVC3 with Windsor

MF

Marco Franssen /

In this blog post I will explain how to inject your dependencies into your controllers in a MVC 3 application with Castle Windsor. First of all you need to create a WindsorInstaller. This class is responsible for the configuration of your IoC container. In the above class the first line registers all your controllers with lifestyle configured to Transient. Depending on the lifestyle you choose Windsor will clean all resources. Other lifestyles are Singleton, Pooled, PerWebRequest, PerThread. T…