Question

I am stumped here. I have struggled to find any viable answer to my question both here and within NSubstitute's documentation.

I'm trying to unit test a service method where, after an object is added to the repository, I access the navigation properties of that object to do other work. When the unit test gets to the point in the code where the new object's navigation properties are accessed, I get a NullReferenceException. I understand WHY I get the exception, because I haven't told it what it should return in the mocking, but what I cannot figure out is HOW to tell the unit test what to return when accessing the navigation properties. This, all because the object is created at run-time.

For brevity sake, I will only include the portions of the methods that are relevant.

// This is the method I am trying to unit test
public void SaveSelectedResultsMeasures(SelectResultsMeasuresViewModel model)
{
    // validate the model
    ...

    var cpf = this.repository.GetCareerPlanningFormByID(model.CareerPlanningFormID);

    model.ResultsMeasuresSections.ToList().ForEach(resultsMeasuresSection =>
    {
        var selectedResultsMeasuresSection = cpf.SelectedResultsMeasuresSections
                                                .Where(o => o.ResultsMeasuresSectionID == resultsMeasuresSection.SectionID)
                                                .ToMaybe();

        if (resultsMeasuresSection.IsSelected)
        {
            if (!selectedResultsMeasuresSection.HasValue)
            {
                // here is where the new object is being created and added to the repository
                cpf.SelectedResultsMeasuresSections.Add(new SelectedResultsMeasuresSection
                {
                    CareerPlanningFormID = cpf.CareerPlanningFormID,
                    IsDeleted = false,
                    ResultsMeasuresSectionID = resultsMeasuresSection.SectionID,
                    RepeatNumberOfOccurrences = resultsMeasuresSection.RepeatNumberOfOccurrences ?? 1
                });
                this.repository.Commit();

                // now that the add has been committed, i get the object i just created
                var newSelectedResultsMeasuresSection = cpf.SelectedResultsMeasuresSections
                    .Where(o => o.ResultsMeasuresSectionID == resultsMeasuresSection.SectionID)
                    .Single();

                // here is where i access the navigation properties of that object
                // this works just fine, just need to figure out how to unit test it so that i can continue to test the code inside the block
                // when the unit test hits this point, this is where I get the NullReferenceException
                newSelectedResultsMeasuresSection.ResultsMeasuresSection.ResultsMeasures.ToList().ForEach(rm =>
                {
                    // code here...
                }
            }
        }
    }
}

// This is the unit test
[TestClass]
[ExcludeFromCodeCoverage]
public class CareerPlanningFormServiceTest
{
    private readonly IPartnerPerformanceManagementConfiguration configuration;
    private readonly IPartnerPerformanceManagementRepository repository;
    private readonly IPrincipal currentUser;
    private readonly CareerPlanningFormService target;

    public CareerPlanningFormServiceTest()
    {
        this.configuration = Substitute.For<IPartnerPerformanceManagementConfiguration>();
        this.repository = Substitute.For<IPartnerPerformanceManagementRepository>();
        this.currentUser = Substitute.For<IPrincipal>();

        this.target = new CareerPlanningFormService(
            this.configuration,
            this.repository,
            this.currentUser);
    }

    /// <summary>
    /// Tests the SaveSelectedResultsMeasures method.
    /// Test success when adding a new SelectedResultsMeasuresSection
    /// </summary>
    [TestMethod]
    [TestCategory("CareerPlanningFormService")]
    [TestCategory("CareerPlanningFormService - SaveSelectedResultsMeasures")]
    public void SaveSelectedResultsMeasures_Test_Success_Add_SelectedResultsMeasuresSection()
    {
        // Arrange
        var model = new SelectResultsMeasuresViewModel
        {
            CareerPlanningFormID = 1,
            ResultsMeasuresSections = new List<ResultsMeasuresSectionModel>
            {
                new ResultsMeasuresSectionModel
                {
                    SectionID = 1,
                    IsRequired = false,
                    IsSelected = true,
                    ActionPlanLabels = new List<ActionPlanLabelModel>
                    {
                        new ActionPlanLabelModel
                        {
                            LabelID = 1,
                            IsSelected = true,
                        }
                    },
                    CanRepeat = false
                },
                new ResultsMeasuresSectionModel
                {
                    SectionID = 2,
                    IsRequired = false,
                    IsSelected = true,
                    ActionPlanLabels = new List<ActionPlanLabelModel>(),
                    CanRepeat = true,
                    RepeatNumberOfOccurrences = 2
                }
            }
        };
        var cpf = new CareerPlanningForm
        {
            CareerPlanningFormID = 1,
            SelectedResultsMeasuresSections = new EntitySet<SelectedResultsMeasuresSection>()
        };
        this.repository.GetCareerPlanningFormByID(model.CareerPlanningFormID).Returns(cpf);

        // Act
        this.target.SaveSelectedResultsMeasures(model);

        // Assert
        this.repository.Received(8).Commit();
        Assert.AreEqual(2, cpf.SelectedResultsMeasuresSections.Count);

        Assert.AreEqual(1, cpf.SelectedResultsMeasuresSections.Where(o => o.ResultsMeasuresSectionID == 1).Single().SelectedActionPlanLabels.Count());
        Assert.AreEqual(1, cpf.SelectedResultsMeasuresSections.Where(o => o.ResultsMeasuresSectionID == 1).Single().ResultsMeasuresSectionOccurrences.Count());

        Assert.AreEqual(0, cpf.SelectedResultsMeasuresSections.Where(o => o.ResultsMeasuresSectionID == 2).Single().SelectedActionPlanLabels.Count());
        Assert.AreEqual(2, cpf.SelectedResultsMeasuresSections.Where(o => o.ResultsMeasuresSectionID == 2).Single().ResultsMeasuresSectionOccurrences.Count());
    }
}

If there is anything else I can provide that will help, let me know.

Was it helpful?

Solution

It looks like you need to add ResultsMeasures to the ResultsMeasuresSections you're returning in the test?

Before you go further along that path though, it looks like you're doing a lot of work to simulate the behaviour of LINQ-to-SQL. I'd suggest dropping the unit tests that test the specific mechanics of what the code does, and switch to a test that hits a real or in-memory database to make sure your code has the behaviour you want. For example, given you've saved results measures for a CPF, make sure retrieving that CPF has the information you require.

The downside with testing things like .Received(8).Commit() is that you are testing against how you know/expect the other system to behave, not whether the code is actually behaving as required. I feel it is the latter point that is most valuable to us when testing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top