0

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?

Edit

Exception:

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

StackTrace:

Test Name:  DeleteOrganisations_Should_Not_Delete_Data_When_OrganisationId_Not_Exist
Test FullName:  MyProject.Test.Api.Administration.Organisation.DeleteOrganisations.DeleteOrganisations_Should_Not_Delete_Data_When_OrganisationId_Not_Exist
Test Source:    C:\Users\User\Documents\Visual Studio 2017\Projects\MyProject\MyProject.Test\Api\Administration\Organisation\DeleteOrganisations.cs : line 42
Test Outcome:   Failed
Test Duration:  0:00:02.48

Result StackTrace:  at MyProject.Api.Controllers.Administration.OrganisationController.<DeleteOrganisations>d__8.MoveNext() in C:\Users\User\Documents\Visual Studio 2017\Projects\MyProject\MyProject.Api\Controllers\Administration\OrganisationController.cs:line 114
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at MyProject.Test.Api.Administration.Organisation.DeleteOrganisations.<Test>d__4.MoveNext() in C:\Users\User\Documents\Visual Studio 2017\Projects\MyProject\MyProjectTest\Api\Administration\Organisation\DeleteOrganisations.cs:line 49
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context)
Result Message: System.NullReferenceException : Object reference not set to an instance of an object.
5
  • @Will I didn't include any exception details because I think it doesn't have any useful information, but I have edited my question with exception and stacktrace Commented Jul 7, 2017 at 19:17
  • No useful information? Ha ha hah. Your problem is right here: Organisation.DeleteOrganisations. You're waiting on something in that method, and whatever it is is throwing the exception. This has absolutely nothing to do with your test method. Go put a breakpoint in DeleteOrganizations, debug the test and see what's null. Commented Jul 7, 2017 at 19:22
  • Lets pretend I didnt say that :) Commented Jul 7, 2017 at 19:30
  • Oh, no. Screenshotted, so don't bother deleting your comment. This shall haunt you for the rest of your career. On a good note, you'll never not check the callstack to find out where the problem is. Out of shame and self loathing, yes, but you'll be able to fix your bugs faster. :) Commented Jul 7, 2017 at 19:36
  • 1
    Lmao, thanks for pointing me in the right direction, stay safe mister Commented Jul 7, 2017 at 19:41

1 Answer 1

2

Facepalm - I forgot to inject the localizer class in the testbase. Sorry folks my bad.

This line right here:

if (organisations == null || organisations.Count == 0)
{
    return Ok(new GenericResultModel(_localizer["Could_not_find_organisations"]));
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.