使用 Moq 模拟 NHibernate ISession

发布于 2024-08-13 16:50:47 字数 2367 浏览 4 评论 0原文

我正在使用 NHibernate、ASP.NET MVC 2.0 和 StructureMap 启动一个新项目,并使用 NUnit 和 Moq 进行测试。对于每个控制器,我都有一个公共构造函数,其中注入了 ISession。应用程序本身工作得很好,但就单元测试而言,我基本上必须模拟 ISession 才能测试控制器。

当我尝试使用 MOQ 模拟 ISession 时,我收到以下错误消息:

仅支持属性访问 在中间调用中

我的问题似乎是期望框架 CreateQuery 方法中的用户列表,但在谷歌搜索后我现在更清楚了。

我有两个问题:

1)这是模拟 ISession 依赖注入的错误方法吗

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);
            }

请注意,我很确定我可以创建一个硬的编码的用户列表(而不是模拟单个用户并将其添加到列表中),但我想我会保留现在的代码。

此外,此特定控制器的 Index 操作本质上执行上面模仿的 CreateQuery 调用,以返回数据库中的所有用户。这是一个人为的例子 - 不要阅读任何细节。

预先感谢您的帮助

编辑:在回复以下评论时,我添加了错误的堆栈跟踪。此外,User 类上的所有属性都是虚拟的。

测试用例 'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView' 失败:System.NotSupportedException: 仅支持属性访问 在中间调用 设置。不支持的表达 Framework.CreateQuery(“来自用户”)。 在 Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression) 米)在 Moq.ExpressionVisitor.Visit(表达式 经验)在 Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression) 米)在 Moq.ExpressionVisitor.Visit(表达式 经验)在 Moq.Mock.AutoMockPropertiesVisitor.SetupMocks(表达式 表达式)在 Moq.Mock.GetInterceptor(LambdaExpression lambda,模拟模拟)在 Moq.Mock.<>c__DisplayClass122.b__11() 在 Moq.PexProtector.Invoke[T](Func1 函数)在 Moq.Mock.Setup[T1,TResult](模拟模拟, 表达式1 表达式)位于 Moq.Mock1.Setup[TResult](表达式`1 表达) 控制器\UserControllerTest.cs(29,0): 在 Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

I am starting a new project with NHibernate, ASP.NET MVC 2.0 and StructureMap and using NUnit and Moq for testing. For each of my controllers I have a single public constructor into which an ISession is being injected. 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.

When I attempt to Mock the ISession with MOQ i get the following error message:

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.

I have two questions:

1) Is this the WRONG way to mock dependency injection of an ISession

2) Is there a way to modify the code so that it can successfully return my list

            [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. 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.

TestCase
'Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView'
failed: System.NotSupportedException :
Only property accesses are supported
in intermediate invocations on a
setup. Unsupported expression
framework.CreateQuery("from User").
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__DisplayClass122.<Setup>b__11()
at Moq.PexProtector.Invoke[T](Func
1
function) at
Moq.Mock.Setup[T1,TResult](Mock mock,
Expression1 expression) at
Moq.Mock
1.Setup[TResult](Expression`1
expression)
Controllers\UserControllerTest.cs(29,0):
at
Beta.Tests.Unit.Controllers.UserControllerTest.Details_InValidIndex_ReturnsNotFoundView()

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

苄①跕圉湢 2024-08-20 16:50:47

下面是我想出的解决方案,它似乎工作得很好。再说一遍,我不是在测试 NHibernate,也不是在测试数据库 - 我只是想测试依赖于 NHibernate 的控制器。初始解决方案的问题似乎是这样一个事实:我正在调用方法并在最小起订量设置调用中读取会话的列表成员。我通过将解决方案分解为 QueryMock 和 Session Mock(创建查询返回 IQuery 对象)来分解这些调用。事务模拟也是必要的,因为它是会话的依赖项(在我的例子中)......

        [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);
        }

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. 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. I broke up these calls by breaking the solution into a QueryMock and a Session Mock (create query returns an IQuery object). 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);
        }
岁吢 2024-08-20 16:50:47

人们可能会考虑为单元测试设置不同的配置,而不是模拟Session。此单元测试配置使用快速的进程内数据库,例如 SQLite 或 Firebird。在夹具设置中,您完全从头开始创建一个新的测试数据库,运行脚本来设置表,并创建一组初始记录。在每次测试设置中,您打开一个事务,在测试后拆卸中,您回滚事务以将数据库恢复到之前的状态。从某种意义上说,您并不是在嘲笑 Session,因为这会很棘手,而是在嘲笑实际的数据库。

Rather than mocking the Session, one might consider setting up a different Configuration for unit-tests. This unit-testing Configuration uses a fast, in-process database like SQLite or 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文