单元测试没有可观察状态变化的方法

时间:2010-08-13 09:51:33

标签: c# rhino-mocks mbunit

(C#,Rhino Mocks,MbUnit)。

我有一个名为AccountManager的类,它有一个RegisterUser()方法。此方法返回void但会为任何错误抛出异常。 AccountManager调用IDataRepository调用其AddUser()方法来执行数据库插入。

我正在使用Rhino Mock模拟IDataRepository,并为模拟存储库中引发的异常的给定参数集抛出异常。

[Test]
    public void RegisterKnownUser()
    {
        MockRepository mocks = new MockRepository();
        IDataRepository dataRepository = mocks.StrictMock<IDataRepository>();

        using (mocks.Record())
        {
            Expect.Call(() => dataRepository.AddUser("abc", "abc", "abc@abc.com", "a", "bc")).Throw(
                new InvalidOperationException());
        }

        using (mocks.Playback())
        {
            AccountManager manager = new AccountManager(dataRepository);
            Assert.Throws(typeof (InvalidOperationException), () => manager.RegisterUser("abc", "abc", "abc@abc.com", "a", "bc"));
        }
    }

此测试工作正常。

我的问题是如何处理提供给RegisterUser的args正确有效的情况。真正的IDataRepository不会返回任何内容,也不会抛出任何异常。所以简而言之,AccountManager的状态不会改变。这是否意味着我不需要测试AccountManager.RegisterUser,因为它会导致我无法在测试的类和方法中直接观察到任何内容。模拟中的状态测试对我来说有点味道。我想只要我单独测试IDataRepository.AddUser,那么我就不需要测试AccountManager.RegisterUser输入会导致类中没有任何可观察的输入。

提前致谢。

2 个答案:

答案 0 :(得分:2)

如果AccountManager调用DataPrepository,那么您的测试用例仍会验证某些内容。此处的记录/回放验证是否进行了呼叫。如果未进行呼叫,则测试用例将失败。如果它是两次/错误的args,它将失败。

这可能是一个非常基本的测试用例,但它仍然很好,并且不要求你在mock对象中放置状态。

答案 1 :(得分:0)

您可能希望使用有效参数测试方法,以确保不会抛出任何异常。换句话说,您无法观察状态更改或使用返回值(因为它是无效的),但您可以观察到该方法无异常地运行。

顺便说一下,如果方法没有返回值也没有更改AccountManager状态,它会改变其他方式(如果没有,那么你可能应该从代码中删除方法什么都没有) 例如,它可能会影响DataRepository。或者在数据库中添加记录。在这种情况下,您至少可以测试数据是否已更改或是否已成功添加记录。或者它可能会记录一个事件,说明新用户已注册,因此您可以在测试中检查日志事件是否在此处。

  

我认为只要我单独测试IDataRepository.AddUser,那么我就不需要测试AccountManager.RegisterUser输入会导致类中没有任何可观察的输入

如果AccountManager.RegisterUser除了参数执行之外没有向IDataRepository.AddUser添加任何内容,那么,如果您已经测试IDataRepository.AddUser,则不必测试它。如果它检查参数,调用AddUser 并执行其他操作,最好检查它的作用是否正确。

假设你有:

public void AddUser(string userName, string userMail, string passwordHash)
{
    // [...] Add a user to the database.
}

public void RegisterUser(string userName, string userMail, string passwordHash)
{
    if (string.IsNullOrEmpty(userName)) throw new ArgumentNullException(...);
    if (string.IsNullOrEmpty(userMail)) throw new ArgumentNullException(...);
    if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullException(...);
    if (!Checks.IsValidMail(userMail)) throw new ArgumentException(...);

    this.AddUser(userName, userMail, passwordHash);

    this.SaveToLog(LogEvent.UserRegistered, userName, this.IPAddress);
}

RegisterUser中,通过传递错误的参数并期待异常来测试前四行。不得测试第五行,因为您已经测试过AddUser。最后,必须测试第六行以确保在使用有效参数调用RegisterUser时,将创建日志条目。