2010-08-13 123 views
0

(C#,Rhino Mocks,MbUnit)。单元测试没有可观察状态变化的方法

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

我使用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", "[email protected]", "a", "bc")).Throw(
       new InvalidOperationException()); 
     } 

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

此测试正常工作。

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

在此先感谢。

+0

Ribgy:你能提供你想要描述的测试用例的代码吗? AccountManager会调用dataRepository吗? – 2010-08-13 09:56:41

回答

2

如果AccountManager调用DataPrepository,那么您的测试用例仍然会验证某些内容。此处的录制/播放会验证是否进行了通话。如果调用不成功,测试用例将失败。如果它做了两次/错误的参数,它会失败。

这可能是一个非常基本的测试用例,但它仍然是一个很好的测试用例,并且不要求您将状态置于模拟对象中。

+0

谢谢(还有主马)。我已经走下了测试的路线,不会抛出异常,以获得良好的数据。 – 2010-08-13 10:32:23

0

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

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

我想只要我考IDataRepository.AddUser seperately那么我不应该需要测试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时,会创建日志条目。