为单元测试中的模拟目的即时修改 long 运行 进程的值

Modify on the fly value of long running process for Mocking purposes in Unit test

我有以下方法:

public async Task ScrapeObjects(int page)
{
    try
    {
        while (!isObjectSearchCompleted)
        {
            UriBuilder builder = new UriBuilder(_apiSettings.Value.FundaUrl)
            {
                Query = $"test"
            };
            var result = await _apiCaller.GetResponseAsync<ResponseModel>(builder.Uri).ConfigureAwait(false);
            Thread.Sleep(TimeSpan.FromSeconds(1));

            if (result != null)
            {
                foreach (var obj in result.Objects)
                {
                    UpdateDictionary(propertiesAmount, obj.name);
                }
            }
            if (!fundaResult.Objects.Any())
            {
                isObjectSearchCompleted = true;
            }
            else
            {
                page++;
            }
        }
    }
    catch (HttpRequestException ex)
    {
        if (ex.Message == "Request limit exceeded.")
        {
            Thread.Sleep(TimeSpan.FromSeconds(60));
            ScrapeObjects(page);
        }
        Log.Fatal(ex, ex.Message);
    }
}

我正在尝试对其进行单元测试。然而,这是一个很长的 运行 过程。我可以控制 _apiCaller 因为我在嘲笑它。 我想做的是测试以下两种情况:

1) _apiCaller returns 第一次正确结果。我的字典是通过 UpdateDictionary 更新的。第二次调用 returns 没有结果,因此整个方法结束。我测试的目的是确保我的词典已经更新。

2) _apiCaller returns 第一次正确结果。我的字典是通过 UpdateDictionary 更新的。第二次调用反而抛出异常,我的方法终止。我测试的目的是确保我的词典已经更新。

在这两种情况下,我都需要即时更改我的方法(我的模拟 _apiCaller)的行为,或者至少在第一次调用它之后。我怎样才能做到这一点?提前致谢!

使用 SetupSequence 调整调用时模拟的行为。

1) the _apiCaller returns the correct result the first time. My dictionary is updated through UpdateDictionary. The second call instead returns no result and therefore the whole method reaches its end. The goal of my test is to ensure that my dictionary has been updated.

apiMock.SetupSequence(_ => _.GetResponseAsync<ResponseModel>(It.IsAny<Uri>()))
    .Returns(Task.FromResult(correctResponseModel)) // returns the correct result the first time.
    .Returns(Task.FromResult((ResponseModel)null)); // The second call instead returns no result

2) the _apiCaller returns the correct result the first time. My dictionary is updated through UpdateDictionary. The second call instead throws an exception and my method terminates. The goal of my test is to ensure that my dictionary has been updated.

apiMock.SetupSequence(_ => _.GetResponseAsync<ResponseModel>(It.IsAny<Uri>()))
    .Returns(Task.FromResult(correctResponseModel)) // returns the correct result the first time.
    .Throws(new HttpRequestException("Request limit exceeded.")); //second call throws an exception

也就是说,不要将 async-awaitThread.Sleep 混合使用 Task.Delay

await Task.Delay(TimeSpan.FromSeconds(1));

//...

由于未等待重试调用,您在 catch 中打开了自己不需要的行为。

catch (HttpRequestException ex)
{
    if (ex.Message == "Request limit exceeded.")
    {
        Thread.Sleep(TimeSpan.FromSeconds(60));
        ScrapeObjects(page); //<-- not awaited.
    }
    Log.Fatal(ex, ex.Message);
}

我建议研究重试模式以更好地处理您设计中的这种情况