简体   繁体   English

使用Moq模拟NHibernate ISession

[英]Mocking an NHibernate ISession with Moq

I am starting a new project with NHibernate, ASP.NET MVC 2.0 and StructureMap and using NUnit and Moq for testing. 我正在使用NHibernate,ASP.NET MVC 2.0和StructureMap开始一个新项目,并使用NUnit和Moq进行测试。 For each of my controllers I have a single public constructor into which an ISession is being injected. 对于我的每个控制器,我都有一个公共构造函数,其中注入了一个ISession。 The application itself works just fine, but in terms of unit testing I essentially have to mock an ISession in order to test the controllers. 应用程序本身工作正常,但就单元测试而言,我基本上必须模拟一个ISession才能测试控制器。

When I attempt to Mock the ISession with MOQ i get the following error message: 当我尝试使用MOQ模拟ISession时,我收到以下错误消息:

Only property accesses are supported in intermediate invocations 中间调用仅支持属性访问

It appears that my problem is expecting List of users from the framework CreateQuery method but after googling the issue I am now clearer. 看来我的问题是期待来自框架CreateQuery方法的用户列表,但在谷歌搜索问题之后我现在更清楚了。

I have two questions: 我有两个问题:

1) Is this the WRONG way to mock dependency injection of an ISession 1)这是模拟依赖注入ISession的错误方法吗?

2) Is there a way to modify the code so that it can successfully return my list 2)有没有办法修改代码,以便它可以成功返回我的列表

            [Test]
            public void DummyTest()
            {

                var mock = new Mock<ISession>();
                var loc = new Mock<User>();
                loc.SetupGet(x => x.ID).Returns(2);
                loc.SetupGet(x => x.FirstName).Returns("John");
                loc.SetupGet(x => x.LastName).Returns("Peterson");

                var lst = new List<User> {loc.Object};
                mock.Setup(framework => framework.CreateQuery("from User").List<User>()).Returns(lst);

                var controller = new UsersController(mock.Object);
                var result = controller.Index() as ViewResult;
               Assert.IsNotNull(result.ViewData);
            }

Please note, I am pretty sure I could just create a hard-coded list of users (rather than mocking an individual User and adding it to a list) but figured I'd leave the code as I have it right now. 请注意,我很确定我可以创建一个硬编码的用户列表(而不是模拟单个用户并将其添加到列表中),但我想我现在已经保留了代码。

Also, the Index action of this particular controller essentially executes the CreateQuery call mimicked above to return all users in the database. 此外,此特定控制器的Index操作基本上执行上面模仿的CreateQuery调用以返回数据库中的所有用户。 This is a contrived example - don't read anything into the details. 这是一个人为的例子 - 不要读任何细节。

Thanks in advance for your help 在此先感谢您的帮助

Edit: In reply to the below comment, I am adding the stacktrace for the error. 编辑:在回复以下评论时,我正在添加错误的堆栈跟踪。 Also, all properties on the User class are virtual. 此外,User类上的所有属性都是虚拟的。

TestCase 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' failed: System.NotSupportedException : Only property accesses are supported in intermediate invocations on a setup. TestCase'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView'失败:System.NotSupportedException:在设置的中间调用中仅支持属性访问。 Unsupported expression framework.CreateQuery("from User"). 不支持的表达式framework.CreateQuery(“来自用户”)。 at Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m) at Moq.ExpressionVisitor.Visit(Expression exp) at Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m) at Moq.ExpressionVisitor.Visit(Expression exp) at Moq.Mock.AutoMockPropertiesVisitor.SetupMocks(Expression expression) at Moq.Mock.GetInterceptor(LambdaExpression lambda, Mock mock) at Moq.Mock.<>c__DisplayClass12 2.<Setup>b__11() at Moq.PexProtector.Invoke[T](Func 1 function) at Moq.Mock.Setup[T1,TResult](Mock mock, Expression 1 expression) at Moq.Mock 1.Setup[TResult](Expression`1 expression) Controllers\\UserControllerTest.cs(29,0): at Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView() 位于Moq.Mock.AutoMockPropertiesVisitor的Moq.ExpressionVisitor.Visit(表达式exp)Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m)的Moq.Exkression.Vatitor上的Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m)中的Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m) Moq.Mock上的Moq.Mock.GetInterceptor(LambdaExpression lambda,Mock mock)的.SetupMocks(表达式表达式)。<> c__DisplayClass12 2.<Setup>b__11() at Moq.PexProtector.Invoke[T](Func 1函数)的2.<Setup>b__11() at Moq.PexProtector.Invoke[T](Func )at Moq.Mock.Setup [T1,TResult](模拟模拟,表达式1 expression) at Moq.Mock 1.Setup [TResult](表达式1 expression) at Moq.Mock表达式)Controllers \\ UserControllerTest.cs(29,0):在Beta.Tests。 Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

Below is the solution I came up with which seems to work perfectly. 以下是我提出的解决方案似乎完美无缺。 Again, I am not testing NHibernate and I am not testing the database - I simply want to test the controllers which depend on NHibernate. 同样,我没有测试NHibernate而我没有测试数据库 - 我只是想测试依赖于NHibernate的控制器。 The issue with the initial solution appears to be the fact that I was calling a Method as well as reading the List member of the session in the MOQ setup call. 初始解决方案的问题似乎是我在MOQ设置调用中调用Method以及读取会话的List成员这一事实。 I broke up these calls by breaking the solution into a QueryMock and a Session Mock (create query returns an IQuery object). 我通过将解决方案分解为QueryMock和Session Mock(创建查询返回IQuery对象)来解决这些调用。 A transaction mock was also necessary as it is a dependency (in my case) of the session... 事务模拟也是必要的,因为它是会话的依赖(在我的例子中)...

        [Test]
        public void DummyTest()
        {
            var userList = new List<User>() { new User() { ID = 2, FirstName = "John", LastName = "Peterson" } };
            var sessionMock = new Mock<ISession>();
            var queryMock = new Mock<IQuery>();
            var transactionMock = new Mock<ITransaction>();

            sessionMock.SetupGet(x => x.Transaction).Returns(transactionMock.Object);
            sessionMock.Setup(session => session.CreateQuery("from User")).Returns(queryMock.Object);
            queryMock.Setup(x => x.List<User>()).Returns(userList);

            var controller = new UsersController(sessionMock.Object);
            var result = controller.Index() as ViewResult;
            Assert.IsNotNull(result.ViewData);
        }

Rather than mocking the Session , one might consider setting up a different Configuration for unit-tests. 可以考虑为单元测试设置不同的Configuration ,而不是模拟Session This unit-testing Configuration uses a fast, in-process database like SQLite or Firebird. 此单元测试Configuration使用快速的进程内数据库,如SQLite或Firebird。 In the fixture setup, you create a new test database completely from scratch, run the scripts to set up the tables, and create a set of initial records. 在夹具设置中,您可以从头开始创建新的测试数据库,运行脚本来设置表,并创建一组初始记录。 In the per-test setup, you open a transaction and in the post-test teardown, you rollback the transaction to restore the database to its previous state. 在每次测试设置中,您打开一个事务,并在测试后拆解中,回滚事务以将数据库还原到其先前的状态。 In a sense, you are not mocking the Session , because that gets tricky, but you are mocking the actual database. 从某种意义上说,你不是在嘲笑Session ,因为这很棘手,但是你在嘲笑实际的数据库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM