Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Tests on Method that uses GetEntitiesAync (DocumentDB)

After mocking DocumentDBRepository class with its GetEntitiesAsync() method in the unit test, it is return a null value which is not I expect it to return.

Here's my method that I need to test

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

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

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

        return null;
    }

Here's the unit test of this method, noticed that I set up the GetEntitiesAsync() method and expect it to return a book value. But it is returning null when I ran it:

  [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;
        }

When I ran the unit test and debug inside the GetBooksByBookIdAsyncByLinq() method, it is not getting any results from the books variable and return null without any error.

The interesting thing is, when I change the GetEntitiesAsync() method to RunSQLQueryAsync() method, which means using SQL query instead of Linq, the unit test is returning the correct result.

Here's the method I am testing:

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;
        }

And here's the unit test for this method, noticed that I set up the RunQueryAsync() method and expect to return a book value. And it works:

        [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;
    }

So I am thinking maybe the way I mock GetEntitiesAsync() method is incorrect. But I am not sure why...

Here are the RunQueryAsync() and GetEntitiesAsync() methods for reference:

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;
        }
like image 647
superninja Avatar asked Nov 28 '25 19:11

superninja


1 Answers

The Setup method tries to look at the arguments passed into your method, and only match the specified behavior if those arguments are the same.

When you use GetQueryBySQL, Moq is able to detect that the sqlQuery string is the same (as in object.Equals()) as what's passed in, so it works correctly.

When you use GetEntitiesAsync, Moq looks at the two Expressions and thinks they are different because Expression comparison is based on memory equality. So even though the two x => x.BookID == bookIDs look the same to you and me, they are different Expressions at runtime.

Try using It.IsAny<>() instead:

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

Supposing you get that working, you can use Callback or other strategies to test that the expression passed into GetEntitiesAsync has the behavior you're expecting it to have.

like image 79
StriplingWarrior Avatar answered Dec 01 '25 13:12

StriplingWarrior



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!