覆盖以前设置的Typemock Isolator的功能行为(WillThrow()/ DoInstead())

时间:2011-06-28 14:10:50

标签: c# visual-studio-2010 unit-testing mocking typemock

请查看此代码:

        public UserRepository GetUserRepositoryMock()
        {
            // mock all UserRepository instances.
            var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
            Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

            // make all public UserRepository members throw a NotSupportedException.
            Isolate.WhenCalled(() => userRepositoryMock.Select(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.Save(null)).WillThrow(new NotSupportedException());
            Isolate.WhenCalled(() => userRepositoryMock.Remove(null)).WillThrow(new NotSupportedException());
            // ... et cetera until all public members of UserRepository will throw NotSupportedException.
        }

        [TestMethod]
        public void ActivateUser_UserNotFound_ThrowsException()
        {
            var userRepositoryMock = GetUserRepositoryMock();

            // override one of the public UserRepository members to return a specific value.
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).DoInstead(context =>
            {
                return null;
            });

            // call ActivateUser implementation.
            RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");
        }

这段代码实际 UserRepository 的所有公共成员抛出NotSupportedException。

我想要这段代码 UserRepository 的所有公共成员抛出 NotSupportedException 除了 SelectOne()函数,它返回 null

我想确保 RegistrationServices ActivateUser()功能不会调用除之外的 UserRepository 的任何功能我明确指定的SelectOne()函数。

如果是这样,例如通过更改 ActivateUser()的实现来调用 UserRepository Save(),不改变相应的* ActivateUser_UserNotFound_ThrowsException *测试,我希望测试失败,因为更改可能会引入意外行为。通过这种方式,我可以将我的应用程序与第三方完全隔离,并将编码错误降至最低。

我对此代码及其背后原理的疑问是:

  1. 我如何实现理想的行为?
  2. 我有什么其他选择可以达到预期的行为吗?
  3. 期望行为的基本原则是否有效? 即。我是否应该将整个应用程序与第三方隔离用于测试目的,在调用未预期的函数时引发异常并且仅在明确指定时返回有效数据?

2 个答案:

答案 0 :(得分:2)

注意:我在Typemock工作

您可以通过以下几种方法来测试您想要的内容。 例如,您可以使用Isolate.Verify API确保没有对您的虚假对象进行特定调用。

这将允许您不明确指定其他方法的返回,因为您可以确保它们不会发生:

    [Test, Isolated]
    public void ActivateUser_UserNotFound_ThrowsException()
    {
        var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
        Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

        Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillReturn(null);

        // call ActivateUser implementation.
        RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");

        Isolate.Verify.WasNotCalled(() => userRepositoryMock.Save(null));
        Isolate.Verify.WasNotCalled(() => userRepositoryMock.Remove(null));
    }

Isolator的WhenCalled()方法行为按照定义的顺序排列,这意味着在原始测试中,第一次SelectOne将抛出异常,第二次及其上将返回null

希望有所帮助,如果您有其他问题,请随时通过支持与我们联系,或者在这里!

答案 1 :(得分:1)

我不确定你为什么要这样做。您最好在测试中设置模拟对象,并为每个测试创建一个新的模拟对象。

这看起来似乎更多的工作,但使用上面的方法将使您的测试难以遵循,GetUserRepositoryMock方法越来越有用,因为您添加了更多需要稍微不同的行为的测试。< / p>

如果您想要做的只是验证那些方法没有被调用,那么您可以使用Isolate.Verify.WasNotCalled(x=>x.Select(null));为每个方法执行此操作。我认为这是一种更好的方法,可以设置所有方法来抛出异常。

您可以通过GetUserRepositoryMock方法获取大量布尔值来指定哪些方法可以使用throw NotImplementedException进行模拟,所有默认值均为true,从而获得您想要的内容。然后,您可以使用命名参数来指定您不想设置的参数。这些方面的东西:

    public UserRepository GetUserRepositoryMock(bool mockSelect=true, bool mockSelectOne=true, bool mockSave=true ... etc etc)
    {
        // mock all UserRepository instances.
        var userRepositoryMock = Isolate.Fake.Instance<UserRepository>();
        Isolate.Swap.AllInstances<UserRepository>().With(userRepositoryMock);

        // make all public UserRepository members throw a NotSupportedException.
        if(mockSelect) 
            Isolate.WhenCalled(() => userRepositoryMock.Select(null)).WillThrow(new NotSupportedException());
        if (mockSelectOne)
            Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).WillThrow(new NotSupportedException());
        if(mockSave)
            Isolate.WhenCalled(() => userRepositoryMock.Save(null)).WillThrow(new NotSupportedException());

        // ... et cetera until all public members of UserRepository will throw NotSupportedException where the corresponding flag is true.
    }

然后你可以在测试中调用它,你不希望模拟SelectOne:

    [TestMethod]
    public void ActivateUser_UserNotFound_ThrowsException()
    {
        var userRepositoryMock = GetUserRepositoryMock(mockSelectOne:false);

        // override one of the public UserRepository members to return a specific value.
        Isolate.WhenCalled(() => userRepositoryMock.SelectOne(null)).DoInstead(context =>
        {
            return null;
        });

        // call ActivateUser implementation.
        RegistrationServices.ActivateUser("foo@bar.com", "password", "activation-code");
    }

所以你只需要使用命名参数指定你想要与默认值不同的东西。

我并不是说我特别喜欢这个解决方案,并且使用一些TypeMocks自己的方法可能会有更好的解决方案,但这应该可以满足您的需求。您可以根据标志定制此方法以实际对每个事物进行Isolate.Verify.WasNotCalled()检查。