I have an interface with iterative behaviour, and I am having trouble Mocking that in Rhinomocks. The example interface and class is a very simple version of my problem.
Every time LineReader.Read() is called, the LineReader.CurrentLine() should return a different value -- the next line. This behaviour I haven't been able to reproduce in a mock so far. Thus, it has become a small hobby project of mine which I return to from time to time. I hope you can help me a step further.
internal class LineReader : ILineReader
{
private readonly IList<string> _lines;
private int _countOfLines;
private int _place;
public LineReader(IList<string> lines)
{
_lines = lines;
_countOfLines = lines.Count;
_place = 0;
}
public string CurrentLine()
{
if (_place<_countOfLines)
{
return _lines[_place];
}
else
{
return null;
}
}
public bool ReadLine()
{
_place++;
return (_place < _countOfLines);
}
}
EDIT Incomplete unit test added:
[Test]
public void Test()
{
IList<string> lineListForMock = new List<string>()
{
"A",
"B",
"C"
};
MockRepository mockRepository = new MockRepository();
ILineReader lineReader = mockRepository.Stub<ILineReader>();
//Setup the values here
mockRepository.ReplayAll();
bool read1 = lineReader.ReadLine();
Assert.That(read1, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("A"));
bool read2 = lineReader.ReadLine();
Assert.That(read2, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("B"));
bool read3 = lineReader.ReadLine();
Assert.That(read3, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("C"));
bool read1 = lineReader.ReadLine();
Assert.That(read1, Is.False);
}
This is all you need:
var enumerator = new List<string> { "A", "B", "C" }.GetEnumerator();
var lineReader = MockRepository.GenerateStub<ILineReader>();
lineReader.Stub(x => x.CurrentLine())
.Return("ignored")
.WhenCalled(x => x.ReturnValue = enumerator.Current);
lineReader.Stub(x => x.ReadLine())
.Return(false) // will be ignored
.WhenCalled(x => x.ReturnValue = enumerator.MoveNext());
This seems to do the trick:
[TestFixture]
public sealed class TestIterativeRhinoReturn
{
private int _count;
private int _countOfLines;
private IList<string> _lines;
private string _currentLine;
[SetUp]
public void SetUp()
{
_count = -1;
_lines= new List<string>()
{
"A",
"B",
"C",
null
};
_countOfLines = _lines.Count;
_currentLine = null;
}
[Test]
public void Test()
{
MockRepository mockRepository = new MockRepository();
ILineReader lineReader = mockRepository.DynamicMock<ILineReader>();
lineReader.Stub(r => r.ReadLine()).Callback(new ReadLineDelegate(ReadRecord)).Return(_count < _countOfLines);
lineReader.Stub(r => r.CurrentLine()).Do(new CurrentStringDelegate(ReturnString)).Return(_currentLine);
mockRepository.ReplayAll();
bool read1 = lineReader.ReadLine();
Assert.That(read1, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("A"));
bool read2 = lineReader.ReadLine();
Assert.That(read2, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("B"));
bool read3 = lineReader.ReadLine();
Assert.That(read3, Is.True);
Assert.That(lineReader.CurrentLine(), Is.EqualTo("C"));
bool read4 = lineReader.ReadLine();
Assert.That(read4, Is.False);
Assert.That(lineReader.CurrentLine(), Is.Null);
}
public delegate bool ReadLineDelegate();
private bool ReadRecord()
{
_count++;
return (_lines[_count]!=null);
}
public delegate string CurrentStringDelegate();
private string ReturnString()
{
return _lines[_count];
}
Notice the Lines:
lineReader.Stub(r => r.ReadLine()).Callback(new ReadLineDelegate(ReadRecord)).Return(_count < _countOfLines);
lineReader.Stub(r => r.CurrentLine()).Do(new CurrentStringDelegate(ReturnString)).Return(_currentLine);
and the Delegate methods ReadRecord() and ReturnString(). Now the returnvalue changes for each read.
I don't know if I have the latest version of rhino-mocks but my .Return method does not take a delegate / action --- something that may have been useful for doing what you want.
However, you seem to be closer to state-based testing here so maybe just create your own mock implementation for testing (or stub, or fake --- you choose :)
Then you can control the exact values you are after.
来源:https://stackoverflow.com/questions/6438727/mocking-iterative-behaviour