Paul Sinnema
Paul Sinnema

Reputation: 2792

NHibernate Unit test fails with Null Reference Exception

My test is setup with mocking data like this

var persons = new List<Core.Domain.Person>
{
    new Core.Domain.Person { DisplayName = "Coordinator 1", Email="[email protected]", WurAccount = "coord001" },
    new Core.Domain.Person { DisplayName = "Coordinator 2", Email="[email protected]", WurAccount = "coord002" }
};

this.mockUnitOfWork.Setup(x => x.Query<Core.Domain.Person>()).Returns(persons.AsQueryable);

In the code to test the persons are retrieved like this

private Dictionary<string, Domain.Person> GetPersons(IEnumerable<string> wurAccounts)
{
    var accounts = wurAccounts.ToList();
    return this.session.Query<Domain.Person>()
        .Where(x => accounts.Contains(x.WurAccount))
        .ToDictionary(x => x.WurAccount);
}

When I run the test I get a NullReferenceException here:

at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey](IEnumerable`1 source, Func`2 keySelector)
   at Wur.P2G.Core.Services.PersonSynchronizer.GetPersons(IEnumerable`1 wurAccounts) in C:\Projects\P2G\P2G\Sources\Wur.P2G.Core\Services\PersonSynchronizer.cs:line 112

EDIT

I was able to boile it down to this piece of code that still causes the NullReference Exception:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using NHibernate;
using NHibernate.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Wur.P2G.Core.Services.Tests
{
    public interface IUnitOfWork : IDisposable
    {
        ISession Session { get; }
        IQueryable<T> Query<T>();
        void BeginTransaction();
        void Commit();
        void Rollback();
    }

    public class Person
    {
        public virtual string WurAccount { get; set; }
        public virtual string DisplayName { get; set; }
        public virtual string Email { get; set; }
        public virtual bool HasValidEmail => true;
        public override string ToString() => WurAccount;
    }

    [TestClass()]
    public class PersonSynchronizerTests
    {
        private Mock<IUnitOfWork> mockUnitOfWork;
        private Mock<ISession> mockSession;

        [TestInitialize]
        public void Initialize()
        {
            this.mockUnitOfWork = new Mock<IUnitOfWork>();
            this.mockSession = new Mock<ISession>();
            this.mockUnitOfWork.Setup(x => x.Session).Returns(this.mockSession.Object);
        }

        [TestMethod()]
        public void GetPersonsTest()
        {
            var persons = new List<Person>
            {
                new Person { DisplayName = "Coordinator 1", Email="[email protected]", WurAccount = "coord001" },
                new Person { DisplayName = "Coordinator 2", Email="[email protected]", WurAccount = "coord002" }
            };

            this.mockUnitOfWork.Setup(x => x.Query<Person>()).Returns(persons.AsQueryable);

            var wurAccounts = new[] { "coord001", "coord002" };

            var accounts = wurAccounts.ToList();
            var personsRead = mockSession.Object.Query<Person>()
                .Where(x => accounts.Contains(x.WurAccount))
                .ToDictionary(x => x.WurAccount);
        }
    }
}

Some more text because the stupid editor want it from me.

Upvotes: 0

Views: 344

Answers (1)

Nkosi
Nkosi

Reputation: 247018

While the error message shows that the problem is related to NHibernate.Linq, None of the code shown so far is related to NHibernate unless there is something you are not showing that is using it.

The code shown so far should work as expected as demonstrated by the following minimal, complete and verifiable example based on the original example provided.

[TestClass]
public class PersonSynchronizerTest {
    [TestMethod]
    public void PersonSynchronizer_GetPerson_Should_Return_Two() {
        //Arrange
        var wurAccounts = new[] { "coord001", "coord002" };
        var persons = new List<Person> {
            new Person { DisplayName = "Coordinator 1", Email="[email protected]", WurAccount = wurAccounts[0] },
            new Person { DisplayName = "Coordinator 2", Email="[email protected]", WurAccount = wurAccounts[1] }
        };

        var mockSession = new Mock<ISession>();
        mockSession.Setup(_ => _.Query<Person>()).Returns(persons.AsQueryable); //<-- setup session

        var mockUnitOfWork = new Mock<IUnitOfWork>();
        mockUnitOfWork.Setup(_ => _.Query<Person>()).Returns(persons.AsQueryable);
        mockUnitOfWork.Setup(_ => _.Session).Returns(mockSession.Object); //<-- UoW returns session

        var subject = new PersonSynchronizer(mockUnitOfWork.Object);

        //Act
        var actual = subject.GetPersons(wurAccounts);

        //Assert
        actual.Should()
            .NotBeNull()
            .And.HaveCount(wurAccounts.Length);
    }

    public class PersonSynchronizer {
        private IUnitOfWork unitOfWork;
        private ISession session;

        public PersonSynchronizer(IUnitOfWork uow) {
            this.unitOfWork = uow;
            this.session = unitOfWork.Session;
        }
        public Dictionary<string, Person> GetPersons(IEnumerable<string> wurAccounts) {
            var accounts = wurAccounts.ToList();
            return this.session.Query<Person>()
                .Where(x => accounts.Contains(x.WurAccount))
                .ToDictionary(x => x.WurAccount);
        }
    }

    public class Person {
        public string DisplayName { get; set; }
        public string Email { get; set; }
        public string WurAccount { get; set; }
    }

    public interface IUnitOfWork : IDisposable {
        ISession Session { get; }
        IQueryable<T> Query<T>();
        void BeginTransaction();
        void Commit();
        void Rollback();
    }

    public interface ISession {
        IQueryable<T> Query<T>();
    }
}

The snippet above passes as expected when tested.

I suggest reviewing if there are any NHibernate extension methods being called that may be conflicting with the default Linq Extensions, causing the issues encountered.

Upvotes: 1

Related Questions