题
我是 RhinoMocks 的新手,除了了解幕后发生的事情之外,我还试图掌握其语法。
我有一个用户对象,我们将其称为 User,它有一个名为 IsAdministrator 的属性。IsAdministrator 的值通过另一个类进行评估,该类检查用户的安全权限,并根据这些权限返回 true 或 false。我正在尝试模拟这个 User 类,并伪造 IsAdministrator 的返回值以隔离一些单元测试。
这就是我到目前为止正在做的事情:
public void CreateSomethingIfUserHasAdminPermissions()
{
User user = _mocks.StrictMock<User>();
SetupResult.For(user.IsAdministrator).Return(true);
// do something with my User object
}
现在,我期望 Rhino 会“伪造”对属性 getter 的调用,然后返回 true 给我。这是不正确的吗?目前,由于 IsAdministrator 属性中的依赖项,我遇到了异常。
有人可以解释我如何在这里实现我的目标吗?
解决方案
在我开始讨论这个之前,请先简单说明一下。通常,您希望避免使用“严格”模拟,因为它会导致测试变得脆弱。如果发生您没有明确告诉 Rhino 的任何事情,严格模拟将抛出异常。另外,我认为当您调用创建模拟时,您可能会误解 Rhino 正在做什么。将其视为派生自您定义的 System.Type 或实现您定义的 System.Type 的自定义对象。如果你自己做的话,它看起来像这样:
public class FakeUserType: User
{
//overriding code here
}
由于 IsAdministrator 可能只是 User 类型的公共属性,因此您无法在继承类型中覆盖它。
就你的问题而言,有多种方法可以解决这个问题。您可以将 IsAdministrator 实现为用户类的虚拟属性,如下所示 亚伦詹森 提及如下:
public class User
{
public virtual Boolean IsAdministrator { get; set; }
}
这是一个不错的方法,但前提是您计划从 User 类继承。此外,如果您不想伪造此类中的其他成员,他们也必须是虚拟的,这可能不是所需的行为。
实现此目的的另一种方法是使用接口。如果它确实是您想要 Mock 的 User 类,那么我会从中提取一个接口。你上面的例子看起来像这样:
public interface IUser
{
Boolean IsAdministrator { get; }
}
public class User : IUser
{
private UserSecurity _userSecurity = new UserSecurity();
public Boolean IsAdministrator
{
get { return _userSecurity.HasAccess("AdminPermissions"); }
}
}
public void CreateSomethingIfUserHasAdminPermissions()
{
IUser user = _mocks.StrictMock<IUser>();
SetupResult.For(user.IsAdministrator).Return(true);
// do something with my User object
}
如果你愿意的话,你可以通过使用变得更喜欢 依赖注入和IOC 但基本原则是一致的。通常,您希望您的类依赖于接口而不是具体的实现。
我希望这有帮助。我已经在一个大型项目中使用 RhinoMocks 很长时间了,所以请不要犹豫向我询问有关 TDD 和模拟的问题。
其他提示
确保 IsAdministrator 是虚拟的。
另外,请确保您调用 _mocks.ReplayAll()
_mocks.ReplayAll() 不会执行任何操作。这只是因为您使用了SetupResult.For(),这不算数。使用 Expect.Call() 确保您的代码正确执行所有操作。