Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

After updating moq from 4.10.1 to 4.11, mocking nhibernate session throws a System.NullReferenceException. #955

Closed
ronenfe opened this issue Oct 24, 2019 · 10 comments · Fixed by #1000
Milestone

Comments

@ronenfe
Copy link

ronenfe commented Oct 24, 2019

 public class UnitTest1
    {
        [Fact]
        public void TestMethod1()
        {
            // Arrange
            Guid itemId = Guid.NewGuid();
            var session = new Mock<NHibernate.ISession>();
            var repo = new Repository();
            session.Setup(x => x.QueryOver<Item>().Where(b => b.Id == itemId).List()).Returns(new List<Item>());

            // Act
            var res = repo.GetById(itemId, session.Object);

            // Verify
            //...
        }
    }
  class Repository
    {
        public IList<Item> GetById(Guid id, ISession session)
        {
            var items = session.QueryOver<Item>()
                .Where(b => b.Id == id)
                .List();

            return items;
        }
    }
    public class Item
    {
        public Guid Id { get; set; }
    }

4.11 and above:
getting: System.NullReferenceException: 'Object reference not set to an instance of an object.'

image

4.10.1 and beyond:
works.

Attached is a sample solution. See exception when running the test. Downgrade to 4.10.1 and see error is gone.
UnitTestProject2.zip

@ronenfe ronenfe changed the title After updating moq from 4.10.1 to 4.11, mocking nhibernate session doesn't work After updating moq from 4.10.1 to 4.11, mocking nhibernate session through interface doesn't work Oct 24, 2019
@stakx
Copy link
Contributor

stakx commented Oct 24, 2019

... and what would "doesn't work" mean exactly? Sorry, your report doesn't make it clear enough what exactly the problem is.

Also, please test with the latest version of Moq, which at this time is 4.13.1.

@ronenfe
Copy link
Author

ronenfe commented Oct 24, 2019

I am revising the report but couldn't delete it, give me a while for analyzing.

@ronenfe
Copy link
Author

ronenfe commented Oct 24, 2019

Hi, I edited the description and attached a sample solution.

@ronenfe ronenfe changed the title After updating moq from 4.10.1 to 4.11, mocking nhibernate session through interface doesn't work After updating moq from 4.10.1 to 4.11, mocking nhibernate session through interface throws a System.NullReferenceException. Oct 24, 2019
@ronenfe ronenfe changed the title After updating moq from 4.10.1 to 4.11, mocking nhibernate session through interface throws a System.NullReferenceException. After updating moq from 4.10.1 to 4.11, mocking nhibernate session throws a System.NullReferenceException. Oct 24, 2019
@ronenfe
Copy link
Author

ronenfe commented Oct 25, 2019

Same error for latest version of Moq.

@stakx stakx removed the needs-repro label Oct 25, 2019
@stakx
Copy link
Contributor

stakx commented Oct 25, 2019

Thanks for the repro, @ronenfe. This does look like a regression. Seems like quoted expressions might no longer be treated correctly in all cases. I'll take a closer look soon.

@stakx
Copy link
Contributor

stakx commented Oct 26, 2019

@ronenfe, not a regression, after all! Starting with Moq 4.11.0, matching of multi-part expressions such as your x => x.QueryOver<Item>().Where(b => b.Id == itemId).List() has become more accurate, in earlier versions Moq didn't really match arguments of each individual method part correctly / not at all—now it does, and finds that b => b.Id == itemId from your test and b => b.Id == id inside your repository class are not equal LINQ expression trees. That in turn is due to captured variables (itemId and id), which haven't been evaluated prior to expression tree comparison. That is, the way Moq currently sees it, these expressions are different because they catch different variables (regardless of what values they contain at the time of comparison).

I'll need to check whether it makes sense to evaluate captured variables prior to comparing the expression trees; if yes, your use case can likely be fixed; otherwise, I'll close this issue.

@stakx stakx added this to the 4.13.2 milestone Oct 26, 2019
@ronenfe
Copy link
Author

ronenfe commented Oct 28, 2019

Thanks stakx, is it possible to overcome this with the current version by changing the usage? or I need to wait for next release?

@stakx
Copy link
Contributor

stakx commented Oct 28, 2019

is it possible to overcome this with the current version by changing the usage?

Yes, although the resulting code will be less semantically correct than what you have now. You simply need to make sure that you pass the same (as in object.ReferenceEquals) Expression<Func<Item, bool>> to both .Where(...) calls. That is, create that expression once, store it to a variable that's accessible to both code sites, then use it in both places.

@ronenfe
Copy link
Author

ronenfe commented Oct 28, 2019

I added the expression to the Item class as a property but I also had to extract the itemId to a property as I can't create an expression without having an id variable when creating it.
If you have better suggestion please let me know, these are the changed class and test:

using System;
using System.Collections.Generic;
using Moq;
using Xunit;


namespace UnitTestProject2
{
    public class UnitTest1
    {
        [Fact]
        public void TestMethod1()
        {
            // Arrange
            Guid itemId = Guid.NewGuid();
            var session = new Mock<NHibernate.ISession>();
            var repo = new Repository();
            session.Setup(x => x.QueryOver<Item>().Where(repo.Expression).List()).Returns(new List<Item>());
            // Act
            var res = repo.GetById(itemId, session.Object);
            // Verify
            //...
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using NHibernate;

namespace UnitTestProject2
{
    public class Repository
    {
        public Guid ID { get; set; } = new Guid();
        public Expression<Func<Item, bool>> Expression;


        public Repository()
        {
            Expression = b => b.Id == ID;
        }
        public IList<Item> GetById(Guid id, ISession session)
        {
            ID = id;
            var items = session.QueryOver<Item>()
                .Where(Expression)
                .List();

            return items;
        }
    }
}

@stakx
Copy link
Contributor

stakx commented Apr 18, 2020

There you go @ronenfe—starting with the next version of Moq (4.14.0), your scenario should once again work like it used to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants