【问题标题】:Unit Tests on Method that uses GetEntitiesAync (DocumentDB)使用 GetEntitiesAync (DocumentDB) 的方法的单元测试
【发布时间】:2018-11-05 08:26:37
【问题描述】:

在单元测试中使用GetEntitiesAsync() 方法模拟DocumentDBRepository 类后,它返回一个null 值,我不希望它返回。

这是我需要测试的方法

 public async Task<Books> GetBooksByBookIdAsyncByLinq(string bookId)
    {

        var books = await _individualRepository.GetEntitiesAsync(t => t.BookID == bookId);

        if (individualResponse.Any())
        {
            return individualResponse.FirstOrDefault();
        }

        return null;
    }

这是该方法的单元测试,注意到我设置了GetEntitiesAsync() 方法并期望它返回账面价值。但是当我运行它时它返回null

  [Fact]
        public void Test_GetBooksByBookIdAsyncByLinq()
        {
            //Arrange
            var bookID = "000";

            //Mock DocumentDBRepository
            var mockDocumentDBRepository = new Mock<IRepository<Book>>();
            var expected = Get_BookValue();
            mockDocumentDBRepository.Setup(x => x.GetEntitiesAsync(x => x.BookID == bookID))
                                    .Returns(Task.FromResult(expected));
            var component = new BookComponent(mockDocumentDBRepository.Object);

            //Act
            var result = component.GetBooksByBookIdAsyncByLinq(bookID);
            //Assert
            result.Result.Should().NotBeNull().And.
                 BeOfType<Book>();
        }


        private Book Get_BookValue(){

         IEnumerable<Book> result = new List<Book>
                    { new Book
                    { BookID = "000", BookName = "TestSourceSystemName" } };
                    return result;
        }

当我在GetBooksByBookIdAsyncByLinq() 方法中运行单元测试和调试时,它没有从books 变量中获得任何结果,并且没有任何错误地返回null

有趣的是,当我将GetEntitiesAsync() 方法更改为RunSQLQueryAsync() 方法时,这意味着使用SQL 查询而不是Linq,单元测试返回了正确的结果。

这是我正在测试的方法:

public async Task<Books> GetBooksByBookIdAsyncBySQL(string bookId)
        {

            var books = await _individualRepository.RunSQLQueryAsync("select * from c where c.BookID ==" + bookId);

            if (individualResponse.Any())
            {
                return individualResponse.FirstOrDefault();
            }

            return null;
        }

这里是这个方法的单元测试,注意到我设置了 RunQueryAsync() 方法并期望返回一个账面值。它有效

        [Fact]
                public void Test_GetBooksByBookIdAsyncBySQL()
                {
                    //Arrange
                    var bookID = "000";

                    var sqlQuery = "select * from c where c.BookID ==" + bookId;

                    //Mock DocumentDBRepository
                    var mockDocumentDBRepository = new Mock<IRepository<Book>>();
                    var expected = Get_BookValue();
//mockDocumentDBRepository.Setup(x => x.GetEntitiesAsync(x => x.BookID == bookID))
//                                        .Returns(Task.FromResult(expected));
                    mockDocumentDBRepository.Setup(x => x.RunQueryAsync(sqlQuery))
                                            .Returns(Task.FromResult(expected));
                    var component = new BookComponent(mockDocumentDBRepository.Object);

                    //Act
                    var result = component.GetBooksByBookIdAsyncBySQL(bookID);
                    //Assert
                    result.Result.Should().NotBeNull().And.
                         BeOfType<Book>();
                }
    private Book Get_BookValue(){

     IEnumerable<Book> result = new List<Book>
                { new Book
                { BookID = "000", BookName = "TestSourceSystemName" } };
                return result;
    }

所以我在想我模拟GetEntitiesAsync() 方法的方式可能不正确。但我不知道为什么......

这里有RunQueryAsync()GetEntitiesAsync()方法供参考:

public async Task<IEnumerable<T>> GetEntitiesAsync(Expression<Func<T, bool>> predicate)
        {
                IDocumentQuery<T> query = GetQueryByPredicate(predicate);
                List<T> results = new List<T>();
                while (query.HasMoreResults)
                {
                    results.AddRange(await query.ExecuteNextAsync<T>());
                }
                return results;
        }

   public async Task<IEnumerable<T>> RunQueryAsync(string queryString)
        {
            IDocumentQuery<T> query = GetQueryBySQL(queryString);

            List<T> results = new List<T>();

            while (query.HasMoreResults)
            {
                results.AddRange(await query.ExecuteNextAsync<T>());
            }
            return results;
        }

【问题讨论】:

    标签: linq unit-testing asp.net-core moq azure-cosmosdb


    【解决方案1】:

    Setup 方法尝试查看传递给您的方法的参数,并且仅当这些参数相同时才匹配指定的行为。

    当您使用GetQueryBySQL 时,Moq 能够检测到sqlQuery 字符串与传入的字符串相同(如object.Equals()),因此它可以正常工作。

    当您使用GetEntitiesAsync 时,Moq 会查看这两个 Expression 并认为它们是不同的,因为 Expression 比较是基于内存相等的。所以即使这两个x =&gt; x.BookID == bookIDs 在你我看来是一样的,但它们在运行时是不同的表达式。

    尝试改用It.IsAny&lt;&gt;()

    mockDocumentDBRepository
        .Setup(x => x.GetEntitiesAsync(It.IsAny<Expression<Func<Book, bool>>()))
        .Returns(Task.FromResult(expected));
    

    假设您可以正常工作,您可以使用Callback 或其他策略来测试传递给 GetEntitiesAsync 的表达式是否具有您期望的行为。

    【讨论】:

    • 成功了!!!太感谢了!!!我使用了'mockDocumentDBRepository .Setup(x => x.GetEntitiesAsync(It.IsAny>())) .Returns(Task.FromResult(expected));'因为GetEntitiesAsync函数的参数是Expression&lt;Func&lt;T, bool&gt;&gt;
    • 当然,这是有道理的。我相应地更新了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多