mardi 8 décembre 2015

FakeItEasy Action parameter in UnitTest, but still execute inner Action code

I'm currently making some UnitTests for some new features I've added to our ASP.NET project (no it's not test-driving design). We use the NHibernate framework and use the UnitTest Mock-ing library FakeItEasy.

I have the following class & method which I want to test:

public class Round
{
    public static Round Create(List<Company> activeCompanies, Period period, BusinessUser user,
        BusinessUser systemUser, ISession session, IEntityQuery entityQuery, RoundProcessBuilder processBuilder)
    {
        var round = new Round
        {
            Processes = new List<Process>();
            Period = period,
            CreationDate = DateTime.Now,
            CreatedBy = user
        };

        session.Save(round); // Save the Round in the DB so we can use it's Id in the Processes

        foreach (var company in activeCompanies)
        {
            var companyData = session.Get<CompanyData>(company.Id);
            var processResult = roundProcessBuilder.Build(systemUser, new CreateRoundProcessData(company, round, companyData),
                entityQuery, session);

            processResult.HandleProcess(process =>
                {
                    process.Create(systemUser, DateTime.Now, session, null); // serviceBus can stay null
                    round.Processes.Add(process);
                    // No need to save the session here. If something went wrong we don't want halve of the processes being saved
                    // It's all or nothing
                });
        }

        return round;
    }
}

What I mainly want to test: When I use this Round#Create method with let's say 100 active companies, it should create 100 processes, and each of those processes should contain the RoundId.

This is my UnitTest so far:

[TestFixture]
public class RoundTest
{
    private BusinessUser _systemUser;
    private DateTime _creationDateRound1;
    private List<Company> _activeCompanies;
    private RoundProcessBuilder _roundProcessBuilder;
    private ISession _session;

    [SetUp]
    public void Setup()
    {
        _creationDateRound1 = new DateTime(2015, 10, 5);
        _systemUser = TestHelper.CreateBusinessUser(Role.Create("systemuser", "test", Int32.MaxValue));
        _activeCompanies = new List<Company>
        {
            TestHelper.CreateCompany();
        };
        _roundProcessBuilder = A.Fake<RoundProcessBuilder>();
        _session = A.Fake<ISession>();
    }

    [Test]
    public void TestCreateRoundWithoutPreviousRound()
    {
        var fakeExpectedRound = Round.Create(_activeCompanies, DateTime.Now.ToPeriod(),
            _systemUser, _systemUser, _session, null, _roundProcessBuilder);
        var fakeExpectedRoundData = RoundProcessData.Create(TestHelper.CreateCompany(), fakeExpectedRound, new CompanyData());
        var fakeExpectedProcess = new Process(_systemUser, null, "processName", null, fakeExpectedRoundData, "controllerName", null);
        var processSuccessResult = new ProcessSuccessResult(fakeExpectedProcess);

        A.CallTo(() => _roundProcessBuilder.Build(null, null, null, null))
            .WithAnyArguments()
            .Returns(processSuccessResult);

        A.CallTo(() => processSuccessResult.HandleProcess(A<Action<Process>>.Ignored))
            .Invokes((Action<Process> action) => action(fakeExpectedProcess));
        var round = Round.Create(_activeCompanies, _ceationDateRound1.ToPeriod(),
            _systemUser, _systemUser, _session, null, _roundProcessBuilder);

        Assert.AreEqual(_activeCompanies.Count, round.Processes.Count, "Number of processes");
        Assert.AreEqual(round.Period.Quarter, Math.Ceiling(_creationDateRound1.Month / 3.0m), "Quarter");
        Assert.AreEqual(round.Period.Year, round.Year, "Year");

        // Test if each of the processes knows the RoundId, have the proper state, and are assigned to the systemuser
        //foreach (var process in round.Processes)
        //{
        //    var roundProcessData = process.ProcessData as RoundProcessData;
        //    Assert.IsNotNull(roundProcessData, "All processes should have RoundProcessData-objects as their data-object");
        //    Assert.AreEqual(roundProcessData.Round.Id, round.Id, "RoundId");
        //    Assert.AreEqual(process.Phase.State, PhaseState.Start, "Process state should be Start");
        //    Assert.AreEqual(process.AssignedTo, _systemUser, "AssignedTo should be systemuser");
        //}
    }

    ... // More tests
}

My problem lies in the following code:

A.CallTo(() => processSuccessResult.HandleProcess(A<Action<Process>>.Ignored))
    .Invokes((Action<Process> action) => action(fakeExpectedProcess));

It gives an "The specified object is not recognized as a fake object." error.

The reason I have this part of the code is because the process in the following part was null without it:

processResult.HandleProcess(process => // <- this was null
    {
        process.Create(systemUser, DateTime.Now, session, null);
        round.Processes.Add(process);
    });

PS: I uncommented the foreach with additional checks in my UnitTest because it most likely is pretty useless anyway when I mock the process itself.. My main test is if processes are created and added to the list based on the active companies given.

Aucun commentaire:

Enregistrer un commentaire