【问题标题】:Unit Test - Mock - Issue with Overriddable - Extension Method单元测试 - 模拟 - 可覆盖问题 - 扩展方法
【发布时间】:2019-11-02 15:21:58
【问题描述】:

我正在尝试为以下 Azure 搜索方法编写单元测试:

public async Task Write(ISearchIndexClient indexClient, Search search)
{
    await UploadContents(indexClient, search.SearchContents);
}

private static async Task UploadContents(ISearchIndexClient indexClient, IReadOnlyCollection<dynamic> searchContents) => await Task.Run(() => indexClient.Documents.Index(IndexBatch.Upload(searchContents)));

单元测试代码 1:

public async Task Write_Success()
{
    var searchIndexClientMock = new Mock<ISearchIndexClient>();
    searchIndexClientMock
        .Setup(x => x.Documents.Index(It.IsAny<IndexBatch<Document>>(), It.IsAny<SearchRequestOptions>()))
        .Returns(It.IsAny<DocumentIndexResult>()).Callback(() => IndexBatch.Upload(It.IsAny<IEnumerable<Document>>()));

    var pushFunction = new SearchIndexWriter();

    Search search = new Search();

    await pushFunction.Write(searchIndexClientMock.Object, search);

    //Assert, Verify checks
}

我收到以下错误:

消息:System.NotSupportedException:不支持的表达式:... => ....Index(It.IsAny>(), It.IsAny()) 扩展方法(此处:DocumentsOperationsExtensions.Index)不能用于设置/验证表达式。

单元测试代码 2:

public async Task Write_Success()
{
    var searchIndexClientMock = new Mock<ISearchIndexClient>();
    searchIndexClientMock
        .SetupGet(x => x.Documents).Returns(It.IsAny<IDocumentsOperations>());

    var pushFunction = new SearchIndexWriter();

    var search = new Search()
    {
        SearchContents = new List<dynamic>(),
    };

    await pushFunction.Write(searchIndexClientMock.Object, search);

    //Verify, Assert logic
}

我收到以下错误:

消息:System.NullReferenceException:对象引用未设置为对象的实例。
    在 Microsoft.Azure.Search.DocumentsOperationsExtensions.IndexAsync[T](IDocumentsOperations 操作,IndexBatch^1 批处理,SearchRequestOptions searchRequestOptions,CancellationToken cancelToken)
    在 Microsoft.Azure.Search.DocumentsOperationsExtensions.Index[T](IDocumentsOperations 操作,IndexBatch^1 批处理,SearchRequestOptions searchRequestOptions)

如何测试上传功能?

【问题讨论】:

    标签: c# azure unit-testing mocking moq


    【解决方案1】:

    您基本上是在尝试测试第 3 方依赖项的功能。尽管这些依赖项具有可以模拟的抽象,但它们需要进行不必要的设置来隔离它们以进行单元测试,这对我来说是一种代码味道。

    我建议抽象出第 3 方依赖

    private readonly ISearchService service;
    
    //...assuming service injected
    public SearchIndexWriter(ISearchService service) {
        this.service = service;
    }
    
    public Task Write(ISearchIndexClient indexClient, Search search) {
        return service.UploadContents(indexClient, search.SearchContents);
    }
    

    尽量避免与静态关注点紧密耦合,因为这会使孤立的单元测试变得困难。

    服务定义可能如下所示

    public interface ISearchService {
        Task UploadContents(ISearchIndexClient indexClient, IReadOnlyCollection<dynamic> searchContents);
    }
    

    通过一个简单的实现来包装外部依赖

    public class SearchService : ISearchService  {
        private Task UploadContents(ISearchIndexClient indexClient, IReadOnlyCollection<dynamic> searchContents) 
            =>  indexClient.Documents.IndexAsync(IndexBatch.Upload(searchContents));
    }
    

    不要再为尝试测试您无法控制的代码而感到压力了。而是专注于您可以控制的逻辑。

    public async Task Write_Success() {
        //Arrange
        var serviceMock = new Mock<ISearchService>();
        serviceMock
            .Setup(_ => _.UploadContents(It.IsAny<ISearchIndexClient>(), It.IsAny<IReadOnlyCollection<It.AnyType>>())
            .ReturnsAsync(new object());
    
        var searchIndexClientMock = Mock.Of<ISearchIndexClient>();
    
        var pushFunction = new SearchIndexWriter(serviceMock.Object);
    
        Search search = new Search();
    
        //Act
        await pushFunction.Write(searchIndexClientMock.Object, search);
    
        //Assert, Verify checks
        //...
    }
    

    【讨论】:

      【解决方案2】:

      根据文档,

      https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.search.idocumentsoperations?view=azure-dotnet

      只有一个方法Index,它有两个参数。

      UploadContent 方法中,Index 方法只传递了一个参数。这两个项目是否存在版本差异?

      还有……

      searchIndexClientMock
              .SetupGet(x => x.Documents).Returns(It.IsAny<IDocumentsOperations>());
      

      在设置中,返回应该是一个具体的实例,而不是It.IsAny&lt;IDocumentsOperations&gt;()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-01
        • 1970-01-01
        • 2014-05-26
        • 1970-01-01
        • 2013-04-16
        • 2011-04-02
        • 1970-01-01
        相关资源
        最近更新 更多