Rhino Mock 执行yield return
我正在尝试编写一个单元测试来检查解析错误。我从文件中流式传输数据,解析它并返回解析结果并返回结果,然后将其传递到数据层以进行批量插入。
我在模拟对数据层的调用时遇到问题。因为它被嘲笑,所以它实际上从未枚举收益返回中的值,因此我的解析方法永远不会执行。
public class Processor
{
public IUnityContainer Container { get; set; }
public void ProcessFile(Stream stream)
{
var datamanager = Container.Resolve<IDataManager>();
var things = Parse(stream);
datamanager.Save(things);
}
IEnumerable<string> Parse(Stream stream)
{
var sr = new StreamReader(stream);
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
// do magic
yield return line;
}
}
}
我尝试过类似的方法,但显然不起作用。
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void ProcessFile_InvalidInput_ThrowsException()
{
var mock = new MockRepository();
var stream = new MemoryStream();
var streamWriter = new StreamWriter(stream);
streamWriter.WriteLine("\\:fail");
streamWriter.Flush();
stream.Position = 0;
var datamanager = mock.Stub<IDataManager>();
TestContainer.RegisterInstance(datamanager);
var repos = new ProcessingRepository();
TestContainer.BuildUp(repos);
using (mock.Record())
{
Expect.Call(file.InputStream).Return(stream);
Expect.Call(delegate() { repos.Save(new List<string>()) }).IgnoreArguments();
}
using (mock.Playback())
{
repos.ProcessFile(stream);
}
}
I'm trying to write a unit test to check for parsing errors. I'm streaming data in from a file, parsing it and returning the parsed result with yield return, then passing it to a data layer to bulk insert.
I'm having trouble mocking out the call to the data layer. Because it's mocked it never actually enumerates the values from the yield return and thus my parsing method never executes.
public class Processor
{
public IUnityContainer Container { get; set; }
public void ProcessFile(Stream stream)
{
var datamanager = Container.Resolve<IDataManager>();
var things = Parse(stream);
datamanager.Save(things);
}
IEnumerable<string> Parse(Stream stream)
{
var sr = new StreamReader(stream);
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
// do magic
yield return line;
}
}
}
I tried something like this which obviously doesn't work.
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void ProcessFile_InvalidInput_ThrowsException()
{
var mock = new MockRepository();
var stream = new MemoryStream();
var streamWriter = new StreamWriter(stream);
streamWriter.WriteLine("\\:fail");
streamWriter.Flush();
stream.Position = 0;
var datamanager = mock.Stub<IDataManager>();
TestContainer.RegisterInstance(datamanager);
var repos = new ProcessingRepository();
TestContainer.BuildUp(repos);
using (mock.Record())
{
Expect.Call(file.InputStream).Return(stream);
Expect.Call(delegate() { repos.Save(new List<string>()) }).IgnoreArguments();
}
using (mock.Playback())
{
repos.ProcessFile(stream);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
一种最佳解决方案是将“//do magic”中发生的内容放在一个单独的方法中,以便可以对其进行单独的单元测试,而无需从处理 StreamReader 的 while 循环内部进行调用。
您看到的问题是由于枚举的惰性求值造成的。由于您的测试代码实际上都没有枚举“事物”,因此永远不会处理“在幕后”构建的用于处理迭代器块的状态机。
您需要枚举项目才能实际执行 Parse 方法中的逻辑。您可以使用 Rhino.Mocks“WhenCalled”方法来执行此操作(我正在显示 AAA 语法,因为我不记得如何使用记录/重播语义):
注意:这是未经测试的代码
发生的情况是,当调用存根上的 Save 方法时,会向“WhenCalled”传递一个参数 (m),其中包含有关所调用方法的信息。获取第一个参数(事物),将其转换为 IEnumerable并获取其计数。这将强制对可枚举进行评估。
One optimal solution would be to put the stuff that happens in "//do magic" in a separate method so it can be unit tested in isolation -- without the need of being called from inside a while loop that is processing a StreamReader.
The issue you're seeing is due to lazy evaluation of the enumeration. Since none of your test code is actually enumerating the "things", the state machine that is built up "behind the scenes" to handle the iterator block is never processed.
You'll need to cause the items to be enumerated in order to actually execute the logic in the Parse method. You can do this my using Rhino.Mocks "WhenCalled" method (I'm showing the AAA syntax since I don't recall how to use the record/replay semantics):
NOTE: This is untested code
What happens is that when the Save method on your stub is called, the "WhenCalled" is passed a parameter (m) which contains information about the method called. Grab the first argument (things), cast it to
IEnumerable<string>
and get its count. This will force an evaluation of the enumerable.