验证模拟方法使用特定的CancellationTokenSouce,它也是模拟的

时间:2012-09-04 21:40:49

标签: c# c#-4.0 moq cancellationtokensource

我试图验证特定的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 =&gt; x.SetupCsvFileVerification(.entity.Object,(Object).source.Object.Token) 没有配置设置。

源代表的类如下:

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。我不知道自己做错了什么。

任何指示/协助都将不胜感激。

由于

修改

1 个答案:

答案 0 :(得分:2)

乍一看,它看起来应该可以正常工作但是你可能不会尝试:

将验证的匹配条件更改为 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和令牌以返回c​​ts.Token
这些被评估为现在,获取被告知返回X和A.

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


通过更改设置以使用lambdas,我们告诉他们使用'当你调用'时cts指向的值是什么,所以序列现在是

设置get - 不评估CTS或令牌值 呼叫验证
  - 使用回调设置cts
  - get source =&gt;评估,使用新设定值(Y)
  - get token =&gt;评估,使用新设定值(B)
验证=&gt;与B比较,通过

其他想法

CancellationTokenSource是否仅存在以支持测试?

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

轻微的狡辩 - 随意忽略

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

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

是多余的。它不会返回任何内容,因此它不适用于存根,并且您将在以后明确验证该调用。

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