【问题标题】:Constructor arguments cannot be passed for interface mocks (Mock IFeedResponse)不能为接口模拟传递构造函数参数(模拟 IFeedResponse)
【发布时间】:2018-08-07 22:38:26
【问题描述】:

我正在尝试为DocumentDBRepository 分页代码编写单元测试。由于FeedResponse 中涉及到延续令牌,我需要模拟FeedResponse 以便为FeedResponse.ContinuationToken 赋予一些价值。但问题是我收到一条错误消息:

消息:System.ArgumentException:构造函数参数不能 为接口模拟传递。

这是否意味着我无法模拟FeedResponse?还是我使用FeedResponse的方式不对?

这是我的代码:

var response = new Mock<IFeedResponse<T>>(expected);
response.Setup(_ => _.ResponseContinuation).Returns(It.IsAny<string>());
var mockDocumentQuery = new Mock<IFakeDocumentQuery<T>>();

mockDocumentQuery
    .SetupSequence(_ => _.HasMoreResults)
    .Returns(true)
    .Returns(false);

mockDocumentQuery
    .Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
    .Returns((Task<FeedResponse<T>>)response.Object);

当我调试时,断点停在var response = new Mock&lt;IFeedResponse&lt;T&gt;&gt;(expected);,然后错误发生了。

【问题讨论】:

  • 你正在模拟接口,所以你不需要在创建模拟时传递类的构造函数参数。你只需要做var response = new Mock&lt;IFeedResponse&lt;T&gt;&gt;();
  • @ChetanRanpariya 嗯,因为 expected 是 CosmosDB 的预期返回值,那么我应该在哪里传递这个值?
  • 在为 mock 安排期望时需要将其设置为返回值。
  • @WendyW。您可以使用FeedResponse 的实际实例。无需嘲笑它。类似于我在此处stackoverflow.com/a/49911733/5233410 对我们之前的问题之一给出的答案之一
  • @Nkosi 我确实尝试过,但由于我有 FeedResponse.ContinuationToken,它破坏了单元测试。所以我必须弄清楚如何在测试中为 feed 响应放置令牌的值..

标签: c# azure unit-testing moq azure-cosmosdb


【解决方案1】:

错误是因为您在模拟接口并尝试传递构造函数参数。如错误消息所述,这将不起作用。

但是,您可以使用FeedResponse 的实际实例。

鉴于所需的成员不是 virtual 并且也是只读的,您可以考虑对类进行存根并覆盖默认行为,因为 FeedResponse&lt;T&gt; 不是 sealed

例如

public class FeedResponseStub<T> : FeedResponse<T> {
    private string token;

    public FeedResponseStub(IEnumerable<T> result, string token)
        : base(result) {
        this.token = token;
    }

    public new string ResponseContinuation {
        get {
            return token;
        }
    }
}

并在测试中使用存根

//...

var token = ".....";
var response = new FeedResponseStub<T>(expected, token);

//...

mockDocumentQuery
    .Setup(_ => _.ExecuteNextAsync<T>(It.IsAny<CancellationToken>()))
    .ReturnsAsync(response);

//...

【讨论】:

    【解决方案2】:

    这是我在Cosmonaut 中解决它的方法。

    public static FeedResponse<T> ToFeedResponse<T>(this IQueryable<T> resource, IDictionary<string, string> responseHeaders = null)
        {
            var feedResponseType = Type.GetType("Microsoft.Azure.Documents.Client.FeedResponse`1, Microsoft.Azure.DocumentDB.Core, Version=1.9.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
    
            var flags = BindingFlags.NonPublic | BindingFlags.Instance;
    
            var headers = new NameValueCollection
            {
                { "x-ms-request-charge", "0" },
                { "x-ms-activity-id", Guid.NewGuid().ToString() }
            };
    
            if (responseHeaders != null)
            {
                foreach (var responseHeader in responseHeaders)
                {
                    headers[responseHeader.Key] = responseHeader.Value;
                }
            }
    
            var arguments = new object[] { resource, resource.Count(), headers, false, null };
    
            if (feedResponseType != null)
            {
                var t = feedResponseType.MakeGenericType(typeof(T));
    
                var feedResponse = Activator.CreateInstance(t, flags, null, arguments, null);
    
                return (FeedResponse<T>)feedResponse;
            }
    
            return new FeedResponse<T>();
        }
    }
    

    您可以将延续令牌作为字典中的标头键值传递,以设置 FeedResponse 值。 您可以通过将 x-ms-continuation 值设置为令牌来做到这一点。

    请记住,FeedResponseResponseContinuation 属性还考虑了 useETagAsContinuation 值。我在反射调用的构造函数中将其默认为 false。

    如需进一步参考,请检查项目代码以及单元测试的编写方式。

    【讨论】:

      猜你喜欢
      • 2017-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-28
      • 1970-01-01
      • 2021-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多