2

我试图验证特定的 CancellationTokenSource 是否用作方法调用中的实际参数。

        public void DataVerification(Object sender, EventArgs e)
        {
            _entity.PopulateEntityDataVerificationStage(_view.DataTypeInputs, _view.ColumnNameInputs, _view.InitialRow, _view.FinalRow, _view.CurrencyPair, _view.CsvFilePath, _view.ErrorLogFilePath);

            //...

            CancellationTokenSource tempCsvFileVerificationCancellation = new CancellationTokenSource();

            _source.Source = tempCsvFileVerificationCancellation;

           //Want to verify that TempCsvFileVerificationCancellation.Token is passed into the following method.
            _verify.SetupCsvFileVerification(_entity, tempCsvFileVerificationCancellation.Token);

           //...
        } 

以下是我的测试:

    [Test]
    public void DataVerification_SetupCsvFileVerification_CorrectInputs()
    {
        Mock<IMainForm> view = new Mock<IMainForm>();

        Mock<IUserInputEntity> entity = new Mock<IUserInputEntity>();

        Mock<ICsvFileVerification> verify = new Mock<ICsvFileVerification>();
        verify.Setup(x => x.SetupCsvFileVerification(It.IsAny<UserInputEntity>(), It.IsAny<CancellationToken>()));

        CancellationTokenSource cts = new CancellationTokenSource();

        Mock<ICancellationTokenSource> source = new Mock<ICancellationTokenSource>();
        source.SetupSet(x => x.Source = It.IsAny<CancellationTokenSource>()).Callback<CancellationTokenSource>(value => cts = value);
        source.SetupGet(x => x.Source).Returns(cts);
        source.SetupGet(x => x.Token).Returns(cts.Token);

        MainPresenter presenter = new MainPresenter(view.Object, entity.Object, verify.Object, source.Object);

        presenter.DataVerification(new object(), new EventArgs());

        verify.Verify(x => x.SetupCsvFileVerification(entity.Object, source.Object.Token));
    }

错误信息如下:

对模拟的预期调用至少一次,但从未执行:x => x.SetupCsvFileVerification(.entity.Object, (Object).source.Object.Token) 未配置任何设置。

source代表的类如下:

public interface ICancellationTokenSource
{
    void Cancel();

    CancellationTokenSource Source { get; set; }

    CancellationToken Token { get; }
}

public class CancellationTokenSourceWrapper : ICancellationTokenSource
{
    private CancellationTokenSource _source;

    public CancellationTokenSourceWrapper(CancellationTokenSource source)
    {
        _source = source;
    }

    public CancellationTokenSource Source
    {
        get
        {
            return _source;
        }
        set
        {
            _source = value;
        }
    }


    public CancellationToken Token 
    {
        get
        {
            return Source.Token;
        }
    }

    public void Cancel()
    {
        _source.Cancel();
    }
}

当我逐步完成单元测试时,cts 确实被分配了 TempCsvFileVerificationCancellation 的值。source 中的 Token 属性,返回 Source.Token。我不知道我做错了什么。

任何指针/帮助将不胜感激。

谢谢

编辑

4

1 回答 1

3

乍一看,它看起来应该可以工作,但实际上你可以尝试:

把verify上的匹配条件改成It.IsAny(),It.IsAny()看看到底有没有被调用。- 如果不匹配,请调试代码并查看发生了什么 - 如果匹配,则说明匹配问题

尝试entity.Object, It.IsAny() 来查看 UserInputEntity 是否错误。

如果 UserInputEntity 没问题,那么在源设置上放置一个回调。它不需要做任何事情,但允许您在调用 SetupCsvFileVerification 时检查正在使用的值。

更新
我想我找到了它,它与我的以下观点之一有关。您将cts初始化为一个值。它需要有一个初始值,因为设置

source.SetupGet(x => x.Source).Returns(cts); 
source.SetupGet(x => x.Token).Returns(cts.Token); 

没有它会失败,因为它会立即评估 cts.Token 。这意味着这将返回测试中定义的 CancellationTokenSource 中的令牌,而不是生产代码中定义的令牌(并使用回调存储)。

为确保您使用新的 cts 值,您应该将设置更改为

source.SetupGet(x => x.Source).Returns(() => cts);
source.SetupGet(x => x.Token).Returns(() => cts.Token);

这将推迟评估直到使用,即在集合的回调执行之后。

添加了详细信息
问题是评估的时间

假设在测试设置中,您创建了一个具有令牌 A 的 cts X。
然后您设置 Source 以返回 cts 和令牌以返回 cts.Token现在
评估 这些,告诉获取返回 X 和 A。

运行时,cts 被使用回调的一组覆盖(将其称为 Y,令牌为 B),它是验证使用的 B 值,因此失败。


通过将设置更改为使用 lambda,我们告诉他们使用“当你被调用时 cts 指向的任何值”,所以现在的序列是

设置 get - 不评估 CTS 或令牌值 调用验证
- 使用回调设置 cts
- 获取源 => 评估,使用新设置的值 (Y)
- 获取令牌 => 评估,使用新设置的值 (B)
验证 => 比较对阵B,传球

其他想法

CancellationTokenSource 的存在只是为了支持测试吗?

如果是这样,另一种方法是尝试用ICancellationTokenProvider替换它,它将替换新的 CancellationTokenSource(); 调用您的生产代码。这将允许您更轻松地将特定的 CancellationToken 注入代码中,从而验证 SetupCsvFileVerification()。

小问题 - 随意忽略

此外,除非您使用严格的行为,否则设置

verify.Setup(x => x.SetupCsvFileVerification(It.IsAny<UserInputEntity>(), 
                                             It.IsAny<CancellationToken>()));

是多余的。它不返回任何内容,因此它不能用作存根,并且稍后您将显式验证调用。

同样,不需要将“cts”初始化为一个值,将其设置为 null 就足够了,因为设置源的回调应该填充它。

于 2012-09-05T08:37:10.323 回答