【问题标题】:Mocking iterative behaviour模拟迭代行为
【发布时间】:2011-06-22 10:58:54
【问题描述】:

我有一个带有迭代行为的接口,但在 Rhinomocks 中模拟它时遇到了麻烦。示例接口和类是我的问题的一个非常简单的版本。

每次调用 LineReader.Read() 时,LineReader.CurrentLine() 应该返回一个不同的值——下一行。到目前为止,我无法在模拟中重现这种行为。因此,它成为了我的一个小爱好,我时不时会回来。我希望你能帮助我更进一步。

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);
    }

}

添加了不完整的单元测试

    [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);


    }

【问题讨论】:

  • 你为你的单元测试开始了一些代码吗?
  • @Thomas,给我两分钟 :o)

标签: c# unit-testing tdd mocking rhino-mocks


【解决方案1】:

这就是你所需要的:

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());

【讨论】:

  • 太棒了...一定要试试!!谢谢
【解决方案2】:

这似乎可以解决问题:

[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]; 
    }

注意线条:

        lineReader.Stub(r => r.ReadLine()).Callback(new ReadLineDelegate(ReadRecord)).Return(_count < _countOfLines);
        lineReader.Stub(r => r.CurrentLine()).Do(new CurrentStringDelegate(ReturnString)).Return(_currentLine);

以及委托方法 ReadRecord() 和 ReturnString()。现在,每次读取的返回值都会发生变化。

【讨论】:

  • 是的,您需要使用自定义回调来模拟这一点 - Do 和 Callback 接受委托。委托返回的值将是模拟调用时返回的值。更多信息 - goo.gl/77xrc Item#7。您甚至可以访问每次调用中的参数值以确定结果。
  • @Gishu,我很高兴我终于找到了方法。然而,这给我留下的印象是,使用老式手工制作的接口虚拟实现而不是 Rhinomocks 来模拟这种行为通常会容易得多。
【解决方案3】:

我不知道我是否有最新版本的 rhino-mocks,但我的 .Return 方法不接受委托/操作 --- 这可能对做你想做的事情有用。

但是,您似乎更接近于基于状态的测试,所以也许只需创建您自己的模拟实现以进行测试(或存根,或假 --- 您选择:)

然后你可以控制你所追求的确切值。

【讨论】:

  • 使用 DynamicMock 会改变什么吗?
  • Return 方法似乎具有相同的签名,所以没有:(
  • 在下面查看我自己的解决方案。然而,在许多情况下,这将是非常复杂的。通常,按照您的建议,创建自己的模拟实现会容易得多。
  • 听起来您正在寻找WhenCalled 扩展方法。例如,请参阅我的答案。
猜你喜欢
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 2017-08-21
  • 2018-04-16
  • 2015-05-05
  • 2023-03-07
  • 2020-03-15
  • 1970-01-01
相关资源
最近更新 更多