vendredi 7 juillet 2017

c# testing async task - test case ignores await - NullReferenceException

I'm using NUnit, Shouldly and Entity Framework in-memory SQLite database for integration tests and got stuck on two of my test cases that doesn't execute any database calls, (no await executed).

Controller:

[HttpPost("DeleteOrganisations")]
public async Task<IActionResult> DeleteOrganisations([FromBody] DeleteOrganisationsModel model)
{
    //Test case 2 fails here
    if (model == null || !ModelState.IsValid)
    {
        return Ok(new GenericResultModel(_localizer["An_unexpected_error_has_occurred_Please_try_again"]));
    }

    List<OrganisationModel> organisations = (await _organisationService.GetOrganisations(model.OrganisationIds)).ToList();

    //Test case 3 fails here
    if (organisations == null || organisations.Count == 0)
    {
        return Ok(new GenericResultModel(_localizer["Could_not_find_organisations"]));
    }

    StatusModel status = await _statusService.GetStatus(StatusTypeEnum.StatusType.Organisation, StatusEnum.Status.Removed);

    if (status == null)
    {
        return Ok(new GenericResultModel(_localizer["Could_not_find_status"]));
    }

    foreach (OrganisationModel organisation in organisations)
    {
        organisation.StatusId = status.Id;
    }

    List<OrganisationModel> updatedOrganisations = (await _organisationService.UpdateOrganisations(organisations)).ToList();

    if (updatedOrganisations == null || updatedOrganisations.Count == 0)
    {
        return Ok(new GenericResultModel(_localizer["Could_not_remove_organisations"]));
    }

    if (updatedOrganisations.Count == 1)
    {
        return Ok(new GenericResultModel { Id = updatedOrganisations[0].Id });
    }

    return Ok(new GenericResultModel { Count = updatedOrganisations.Count });
}

Test:

[TestFixture]
public class DeleteOrganisations : TestBase
{
    private OrganisationController _organisationController;

    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        //Controller
        _organisationController = new OrganisationController(null, Mapper, OrganisationService, StatusService, AuthenticationService);

        //Object Mother
        ObjectMother.InsertTestData();
    }

    public static IEnumerable TestCases
    {
        get
        {
            yield return new TestCaseData(new DeleteOrganisationsModel { OrganisationIds = new List<int>() { 1 } }, 0, 1).SetName("DeleteOrganisations_Should_Delete_Data_When_OrganisationId_Exist");

            yield return new TestCaseData(new DeleteOrganisationsModel { OrganisationIds = null }, 0, 0).SetName("DeleteOrganisations_Should_Not_Delete_Data_When_Null");
            yield return new TestCaseData(new DeleteOrganisationsModel { OrganisationIds = new List<int>() { 2 } }, 0, 0).SetName("DeleteOrganisations_Should_Not_Delete_Data_When_OrganisationId_Not_Exist");
        }
    }

    [Test, TestCaseSource(nameof(TestCases))]
    public async Task Test(DeleteOrganisationsModel model, int removedOrganisationCountBefore, int removedOrganisationCountAfter)
    {
        //Before
        int resultBefore = Database.Organisation.Include(o => o.Status).Count(o => o.Status.Name == StatusEnum.Status.Removed.ToString());

        resultBefore.ShouldBe(removedOrganisationCountBefore);

        //Delete
        await _organisationController.DeleteOrganisations(model);

        //After
        int resultAfter = Database.Organisation.Include(o => o.Status).Count(o => o.Status.Name == StatusEnum.Status.Removed.ToString());

        resultAfter.ShouldBe(removedOrganisationCountAfter);
    }
}

Test case 1 passes because

StatusModel status = await _statusService.GetStatus(StatusTypeEnum.StatusType.Organisation, StatusEnum.Status.Removed);

is called and the result is awaited.

Test case 2 and 3 fails because there's never an await in the function, (if statement).

Message: System.NullReferenceException : Object reference not set to an instance of an object.

I can probably create another test for results that doesn't execute any await statements and skip the async Task for the new test function, but then I would get the message saying:

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

The test would probably pass but I dont like the idea of two tests plus the syntax error.

What would you do?

Aucun commentaire:

Enregistrer un commentaire