IEnumerable <t>评估和单元测试</t>

时间:2009-06-06 20:54:58

标签: c# .net linq

在阅读了另一个SO线程(Should parameters/returns of collections be IEnumerable<T> or T[]?)之后,我认为我所拥有的单元测试“问题”可能与线程所称的“迟到评估”有关。

我将三种不同但相关的IEnumerables从演示者类传递到我的UI。最好的消息是它有效,但我似乎无法找到一种方法来进行单元测试,最终证明正确的IEnumerable正在通过。

我有以下方法的外观:

public IEnumerable<DynamicDisplayDto> NonProjectDtos
{
    get
    {
        var activities = LeaveTimeActivities.Cast<TimeSheetActivityBase>()
            .Concat(AdministrativeActivities.Cast<TimeSheetActivityBase>());
        return _assembler.ToActivityDtoCollection(activities, _timeSheet);
    }
}

在演示者中,我加载了一个小部件:

private ITimeSheetMatrixWidget _nonProjectActivityMatrix;
public ITimeSheetMatrixWidget NonProjectActivityMatrix {
    set {
        .. 
        // load the activities
        _nonProjectActivityMatrix.Load(Facade.NonProjectDtos);
        ...
    }
}

然后在模拟矩阵上进行测试(使用Rhino Mocks):

[Test]
public void SettingTheWidget_TriggersLoad_NonProjectActivities() {
    var f = _getFacade();
    ...
    // inject the mocked widget & trigger the Load
    var widget = MockRepository.GenerateMock<ITimeSheetMatrixWidget>();
    timeSheetPresenter.ActivityMatrix = widget;

    widget.AssertWasCalled(x => x.Load(f.NonProjectDtos), 
        mo =>mo.IgnoreArguments()); <-- only way not to fail
    //widget.AssertWasCalled(x => x.Load(f.NonProjectDtos)); <-- error
}

如果我查看调试器,我可以看到IEnumerable load arg正在评估Domain.TransferObjects.TimeSheetDtoAssembler + d _1,这也是Rhino消息中方法调用失败的部分。

这与后期评估有关吗?是否有一种相当优雅的方式来更严格地测试它?

我还想更好地理解中间评估,这看起来很像组装它的方法(在上面的外观代码中)。

3 个答案:

答案 0 :(得分:3)

Facade对象在测试和被测试对象中是否相同 - 即,您是否也注入了Facade对象?如果对象不同,这将导致您看到的问题。如果它们是相同的,那么你可以在方法中实现枚举(使用ToList())。

答案 1 :(得分:2)

我只是想解决您的后期评估问题:

CastConcat(以及许多System.Linq.Enumerable方法)都会返回符合IEnumerable<T>的实例。枚举这些实例的实际结果是推迟的。为什么这些结果被推迟而不是急切地确定?

List<int> firstThreePrimes = Enumerable.Range(0, 1000000)
  .Where(i => isPrime(i))
  .Take(3)
  .ToList();

ToList枚举IEnumerable<int>的{​​{1}}结果。 Take(3)未生成任何数字,Range过滤或Where限制,直到Take枚举此结果。

如果急切地确定了Range的结果,我们将生成1000000个整数。

如果急切地确定ToList的结果,那么运行时必须将所有这1000000个整数发送到我们的Where方法。

相反,调用序列看起来像这样。

isPrime

使用调试器很容易确认。只需单步执行即可查看Range return from Range Where return from Where Take return from Take ToList GetEnumerator(Take) GetEnumerator(Where) GetEnumerator(Range) return 0 isPrime(0) is false return 1 isPrime(1) is false return 2 isPrime(2) is true 2 is the first result List.Add(2) return 3 isPrime(3) is true 3 is the second result List.Add(3) return 4 isPrime(4) is false return 5 isPrime(5) is true 5 is the third result List.Add(5) break;

的来电

从检查代码开始,看起来isPrime可能会枚举结果,您可能没有遇到延迟执行。

答案 2 :(得分:2)

Rhino mocks工作得很好,虽然它并不总是能够知道为什么你使用了错误的语法或约束: - )

检查IEnumerable参数是否相等的方法只是使用以下内联约束:

Arg<T>.List.Equal(yourList)

以下是一个完整的例子:

[Test]
public void NonProjectMatrix_Injection_IsLoaded()
{
    _nonProjectMatrix = MockRepository.GenerateMock<ITimeSheetMatrixWidget>();

    var dtos = _facade.NonProjectDtos;
    nonProjectMatrix.Expect(x => x.Load(Arg<IEnumerable<DynamicDisplayDto>>.List.Equal(dtos))).Return(dtos.Count());

        new MatrixEntryService(_facade, _projectMatrix, _nonProjectMatrix, _totalMatrix);

        _nonProjectMatrix.VerifyAllExpectations();
    }

所以问题实际上与延迟执行无关。犀牛只是吐出所有它知道的一个电话没有像我告诉它期望的那样,这就是IEnumerable看待期望失败的时间。

干杯..