对没有可观察到的状态变化的方法进行单元测试
(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", "[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 的输入,这会导致类中没有任何可观察到的内容。
提前致谢。
(C#, Rhino Mocks, MbUnit).
I have a class called AccountManager that has a RegisterUser() method. This method returns void but does throw an exception for any errors. AccountManager calls into an IDataRepository calling its AddUser() method to do a database insert.
I'm mocking the IDataRepository using a Rhino Mock and throwing and exception for a given set of arguments simulating the exception being raised in the repository.
[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"));
}
}
This test works fine.
My question is what to do about the situation where the args supplied to RegisterUser are correct and valid. The real IDataRepository would not return anything nor would it thrown any exceptions. So in short AccountManager's state would not have changed. Does this mean I don't need to test AccountManager.RegisterUser when it would result in nothing I can observe directly in the class and method under test. Testing against state in the mock smells a bit to me. I think as long as I test IDataRepository.AddUser seperately then I shouldn't need to test AccountManager.RegisterUser for inputs that would result in nothing observable in the class.
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果 AccountManager 调用 DataPrepository,那么您的测试用例仍然会验证某些内容。这里的记录/回放验证了是否对其进行了调用。如果不进行调用,测试用例将失败。如果它被创建两次/使用错误的参数,它将失败。
这可能是一个非常基本的测试用例,但它仍然是一个很好的测试用例,并且不需要您将状态放置在模拟对象中。
If AccountManager calls into DataPrepository, then your test case still validates something. The record/playback here validates that a call is made into it. If the call is not made, the test case will fail. If it is made twice/with the wrong args, it will fail.
This may be a very basic test case, but it is still a good one, and doesn't require you to place state in the mock object.
您可能需要使用有效参数测试该方法,以确保它不会引发任何异常。换句话说,您无法观察状态更改或使用返回值(因为它是空的),但您可以观察到该方法运行时没有异常。
顺便说一句,如果该方法不返回值也不更改
AccountManager
状态,它确实会更改其他内容(如果没有,那么您可能应该从代码中删除该方法它什么都不做)。例如,它可能会影响
DataRepository
。或者在数据库中添加一条记录。这种情况下,至少可以测试一下数据是否改变或者记录是否添加成功。或者它可能会记录一个事件,表明新用户已注册,因此您将能够在测试中检查日志事件是否位于此处。如果
AccountManager.RegisterUser
不向添加任何内容IDataRepository.AddUser
除了参数强制之外,是的,如果您已经测试了IDataRepository.AddUser
,则不必测试它。如果它检查参数,调用AddUser
并执行其他操作,那么最好检查它所做的是否正确。假设您有:
在
RegisterUser
中,您通过传递错误的参数并期望出现异常来测试前四行。不得测试第五行,因为您已经测试了AddUser
。最后,必须测试第六行,以确保当您使用有效参数调用RegisterUser
时,会创建日志条目。You may want to test the method with valid arguments to ensure it does not throw any exception. In other words, you cannot observe state change or use a return value (since it's void), but you can observe that the method ran without exceptions.
By the way, if the method does not return a value nor change
AccountManager
state, it does change something otherwise (if not, than you should probably remove from your code the method which does nothing at all).For example, it may affect
DataRepository
. Or add a record in the database. In this case, you can test at least if the data is changed or if the record is added successfully. Or it may log an event saying that the new user was registered, so you will be able, in your tests, to check if the log event is here.If
AccountManager.RegisterUser
adds nothing toIDataRepository.AddUser
except arguments enforcement, than yes, you don't have to test it if you already testedIDataRepository.AddUser
. If it checks arguments, callsAddUser
and does something else, it will be good to check if what it does is correct.Let's say you have:
In
RegisterUser
, you test the first four lines by passing wrong arguments and expecting an exception. The fifth line must not be tested, since you already testedAddUser
. Finally, the sixth line must be tested to ensure that when you callRegisterUser
with valid arguments, the log entry is created.