问题 我该如何设置(Moq设置)


我想测试我返回用户密码问题的代码部分。所以我使用Moq制作了会员提供者的模型。

我认为我不需要向您展示实际代码只是它的测试部分。

// Arrange
var membershipMock = new Mock<MembershipProvider>();
membershipMock.Setup(m => m.GetUser("test", false).PasswordQuestion).Returns("Password");
authentication.Authenticate.Provider = membershipMock.Object;

// Act
var actual = authentication.PasswordRecoveryStep1(It.IsAny<string>());

// Assert
Assert.That(actual, Is.EqualTo("Password"));

所以,当我在Nunit中运行时,我得到了这个:

Test.Controllers.AuthenticationControllerTest.Test_If_Password_Recovery_Setp1_Returns_Users_PasswordQuestion:
System.NotSupportedException : Only property accesses are supported in intermediate invocations on a setup. Unsupported expression m.GetUser("test", False).

at Moq.Mock.AutoMockPropertiesVisitor.VisitMethodCall(MethodCallExpression m)
at Moq.ExpressionVisitor.Visit(Expression exp)
at Moq.Mock.AutoMockPropertiesVisitor.VisitMemberAccess(MemberExpression 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__DisplayClass15`2.<SetupGet>b__14()
at Moq.PexProtector.Invoke[T](Func`1 function)
at Moq.Mock.SetupGet[T1,TProperty](Mock mock, Expression`1 expression)
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)
at Test.Controllers.AuthenticationControllerTest.Test_If_Password_Recovery_Setp1_Returns_Users_PasswordQuestion() in D:\MvcApplication9\Test\Controllers\AuthenticationControllerTest.cs:line 186

所以我猜这是因为我试图访问的属性。我不知道如何设置它。我对lambdas并不是很好(并且还没有找到关于它们的教程)所以我不确定我是否能够以不同的方式安排它以使其工作。

或者,如果我完全错过了标记。


6465
2018-06-27 11:38


起源



答案:


答案是在异常消息中:

...在设置中的中间调用中仅支持属性访问...

尝试这个:

var user = new Mock<MemberShipUser>();
user.SetupGet(x => x.PasswordQuestion).Returns("Password");

membershipMock.Setup(m => m.GetUser("test", false)).Returns(user.Object);

13
2018-06-27 11:58



我不知道100%的是什么。这仅仅意味着第一级属性吗?你也已经超越了这个错误但其他错误。它似乎没有使用模拟对象,它试图实际对数据库运行它。我设置的方式与以往一样,并添加了此行使用的模拟对象:authentication.Authenticate.Provider = membershipMock.Object; - chobo2
好吧我明白了。当我写这些东西时,我完全忘了我使用了Membership.GetUser();所以它永远不会使用我的属性获得真正的提供者或模型提供者。所以它现在有效。但是我不明白它们之间的区别是什么:Membership.GetUser(); //我以前用过的东西现在,因为我在提供者的事情中传递它(因为我认为如果我没有,我将无法制作模拟对象?我可以只使用Membership而不是Membership.Provider?)我似乎得到一个不同的。看起来一样(称为getUser),但仅限于此 - chobo2
有2个重载方法。第一个有6的地方。 - chobo2


答案:


答案是在异常消息中:

...在设置中的中间调用中仅支持属性访问...

尝试这个:

var user = new Mock<MemberShipUser>();
user.SetupGet(x => x.PasswordQuestion).Returns("Password");

membershipMock.Setup(m => m.GetUser("test", false)).Returns(user.Object);

13
2018-06-27 11:58



我不知道100%的是什么。这仅仅意味着第一级属性吗?你也已经超越了这个错误但其他错误。它似乎没有使用模拟对象,它试图实际对数据库运行它。我设置的方式与以往一样,并添加了此行使用的模拟对象:authentication.Authenticate.Provider = membershipMock.Object; - chobo2
好吧我明白了。当我写这些东西时,我完全忘了我使用了Membership.GetUser();所以它永远不会使用我的属性获得真正的提供者或模型提供者。所以它现在有效。但是我不明白它们之间的区别是什么:Membership.GetUser(); //我以前用过的东西现在,因为我在提供者的事情中传递它(因为我认为如果我没有,我将无法制作模拟对象?我可以只使用Membership而不是Membership.Provider?)我似乎得到一个不同的。看起来一样(称为getUser),但仅限于此 - chobo2
有2个重载方法。第一个有6的地方。 - chobo2


我想它引用的中间调用是这样的: m.GetUser("test", false) 因为接下来是 .PasswordQuestion。它的含义是:你不能将一个方法用作中间存根,只能用一个属性。这个特定的框架似乎确实支持中间存根(即在存根定义中构造X.Y,注意点)但大多数其他框架不支持。

存根不是魔术,他们所能做的就是拦截你的电话并用你提供的值替换返回的结果。在您的情况下,您的GetUser存根需要返回 模拟用户,其PasswordQuestion被删除以返回“密码”。

您的代码的另一个问题是您直接嘲笑MembershipProvider。大多数模拟框架的工作方式,如果你模拟一个接口,它们会动态生成一个实现它的类,当你模拟一个类时,它们会生成一个继承它的类并覆盖任何虚方法。但是,如果方法不是虚拟的,则无法覆盖它,因此您可能会观察到混合行为。我建议你看看是否有像IMembershipProvider这样的界面,如果是,请在你的代码中使用它而不是具体的类。


2
2018-06-27 12:24



你知道eu-ge-ne代码似乎正在做什么,但它一直在尝试,但似乎它正在尝试使用我的数据库。 - chobo2
@ chobo2,我更新了答案。别忘了upvote :) - zvolkov
MembershipProvider.GetUser()是抽象方法; MembershipUser.PasswordQuestion是虚拟财产 - eu-ge-ne