Reputation: 2433
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.
Upvotes: 0
Views: 1855
Reputation: 2433
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"]));
}
Upvotes: 2