RhinoMocks:模拟属性获取器的正确方法

时间:2020-03-05 18:57:30  来源:igfitidea点击:

我是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可以"伪造"对属性获取器的调用,然后将true返回给我。这不正确吗?当前,由于IsAdministrator属性中的依赖关系,我遇到了异常。

有人可以在这里解释我如何实现我的目标吗?

解决方案

回答

确保IsAdministrator是虚拟的。

另外,请确保我们调用_mocks.ReplayAll()

回答

在我跳到此之前,请快速注意一下。通常,我们要避免使用"严格"模拟,因为它会使测试变脆。如果发生任何未明确告知Rhino会发生的事情,则严格的模拟将引发异常。另外,我认为我们打电话创建模拟游戏时可能会误解Rhino的行为。可以将其视为自定义对象的派生对象,或者实现我们定义的System.Type。如果我们自己进行操作,则将如下所示:

public class FakeUserType: User
{
    //overriding code here
}

由于IsAdministrator可能只是User类型的公共属性,因此我们不能在继承类型中覆盖它。

就问题而言,有多种方法可以解决此问题。我们可以在用户类上将IsAdministrator实现为虚拟属性,如aaronjensen所述,如下所示:

public class User
{
    public virtual Boolean IsAdministrator { get; set; }
}

这是一种不错的方法,但前提是我们打算从User类继承。同样,如果我们不想在该类上伪造其他成员,那么他们也必须是虚拟的,这可能不是我们想要的行为。

实现此目的的另一种方法是通过使用接口。如果它确实是我们要模拟的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和模拟的问题。

回答

_mocks.ReplayAll()不会执行任何操作。只是因为我们使用的是SetupResult.For()而不算在内。使用Expect.Call()确保代码正确无误。