Unable to perform Async Actions on Mock

2 posts, 0 answers
  1. Bryon
    Bryon avatar
    1 posts
    Member since:
    Jun 2014

    Posted 09 Jun 2014 Link to this post


    So I'm trying to test out a function and it requires the use of a FindAsync function. i've boiled the code down to this example. When I run it, I get a NullReferenceExcetption from the FindAsync method. I believe that this is caused by an internal error in the arrangement of the MockSet.

    [TestMethod()]
    public async Task _ShipmentController_DetailsReturnsHttpNotFoundOnNullShipments()
    {
        //Arrange
        var shipments = new List<ShipmentPlayer>
        {
            new ShipmentPlayer(){ShipmentID = 0},
        }.AsQueryable();
        var mockSet = createMockShipmentPlayerDB(shipments);
        var mockContext = Mock.Create<ExportAttempt4Entities>();
        Mock.Arrange(() => mockContext.ShipmentPlayersGroup).Returns(mockSet);
        //Act
        ShipmentPlayer query = await mockContext.FindAsync(0);
        //Assert
        Assert.IsNotNull(query);
     
    }

    Async set creation method:
    private DbSet<ShipmentPlayer> createMockShipmentPlayerDB(IQueryable<ShipmentPlayer> chks)
    {
        /*
         * This function is a helper to create ASync databases.
         * To modify this for your DB, change the Generics (<T>)
         * to your prefferend generic
         */
        var mockSet = Mock.Create<DbSet<ShipmentPlayer>>();
     
        Mock.Arrange(() => ((IDbAsyncEnumerable<ShipmentPlayer>)mockSet).GetAsyncEnumerator())
            .Returns(new TestDbAsyncEnumerator<ShipmentPlayer>(chks.GetEnumerator()));
     
        Mock.Arrange(() => ((IQueryable<ShipmentPlayer>)mockSet).Provider)
            .Returns(new TestDbAsyncQueryProvider<ShipmentPlayer>(chks.Provider));
     
        Mock.Arrange(() => ((IQueryable<ShipmentPlayer>)mockSet).Expression).Returns(chks.Expression);
        Mock.Arrange(() => ((IQueryable<ShipmentPlayer>)mockSet).ElementType).Returns(chks.ElementType);
        Mock.Arrange(() => ((IQueryable<ShipmentPlayer>)mockSet).GetEnumerator()).Returns(chks.GetEnumerator());
        return mockSet;
    }

    Async Database info:
    internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
        {
            private readonly IQueryProvider _inner;
     
            internal TestDbAsyncQueryProvider(IQueryProvider inner)
            {
                _inner = inner;
            }
     
            public IQueryable CreateQuery(Expression expression)
            {
                return new TestDbAsyncEnumerable<TEntity>(expression);
            }
     
            public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            {
                return new TestDbAsyncEnumerable<TElement>(expression);
            }
     
            public object Execute(Expression expression)
            {
                return _inner.Execute(expression);
            }
     
            public TResult Execute<TResult>(Expression expression)
            {
                return _inner.Execute<TResult>(expression);
            }
     
            public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
            {
                return Task.FromResult(Execute(expression));
            }
     
            public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
            {
                return Task.FromResult(Execute<TResult>(expression));
            }
        }
     
        internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
        {
            public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
                : base(enumerable)
            { }
     
            public TestDbAsyncEnumerable(Expression expression)
                : base(expression)
            { }
     
            public IDbAsyncEnumerator<T> GetAsyncEnumerator()
            {
                return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
            }
     
            IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
            {
                return GetAsyncEnumerator();
            }
     
            IQueryProvider IQueryable.Provider
            {
                get { return new TestDbAsyncQueryProvider<T>(this); }
            }
        }
     
        internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
        {
            private readonly IEnumerator<T> _inner;
     
            public TestDbAsyncEnumerator(IEnumerator<T> inner)
            {
                _inner = inner;
            }
     
            public void Dispose()
            {
                _inner.Dispose();
            }
     
            public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
            {
                return Task.FromResult(_inner.MoveNext());
            }
     
            public T Current
            {
                get { return _inner.Current; }
            }
     
            object IDbAsyncEnumerator.Current
            {
                get { return Current; }
            }
        }

    I found a possible solution in the Moq framework but I can't really translate to Just Mock. The idea is that you would place the following block of code in the set creation function:
    mockSet.Setup(t => t.FindAsync(It.IsAny<int>())).Returns(Task.FromResult(chks));

    I've only been using Just Mock for a few days now, so it might just a be a fundamental issue that I'm missing.
  2. Kaloyan
    Admin
    Kaloyan avatar
    872 posts

    Posted 12 Jun 2014 Link to this post

    Hi Bryon,

    I am happy that you are interested in our product.

    About the issue you are experiencing, the Moq line you have posted should be fully supported in JustMock, with the following syntax:
    Mock.Arrange(() => mockSet.FindAsync(Arg.AnyInt)).Returns(Task.FromResult(chks));

    However, in your test I see that you are acting on a mocked object (mockContext). And as the default mock behavior in JustMock is RecursiveLoose, it is normal for the FindAsync method to return a non-null object, no matter the arrangements on the mockSet. In other words, written like this the test does not execute the original FindAsync logic. What I would suggest is to use a partial mock for the ExportAttempt4Entitiese class, like this:
    [TestMethod()]
    public async Task _ShipmentController_DetailsReturnsHttpNotFoundOnNullShipments()
    {
        //Arrange
        var shipments = new List<ShipmentPlayer>
        {
            new ShipmentPlayer(){ShipmentID = 0},
        }.AsQueryable();
        var mockSet = createMockShipmentPlayerDB(shipments);
        var mockContext = new ExportAttempt4Entities();
        Mock.Arrange(() => mockContext.ShipmentPlayersGroup).Returns(mockSet);
        //Act
        ShipmentPlayer query = await mockContext.FindAsync(0);
        //Assert
        Assert.IsNotNull(query);
      
    }

    I hope this helps. Please, do not hesitate to write back if you need further assistance or you have more questions.

    Regards,
    Kaloyan
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. DevCraft R3 2016 release webinar banner
Back to Top