akhil
akhil

Reputation: 23

X unit test with fakeitesay faking async methods asp.net

The return type of result and expected are different unable to fake an async method my code on x unit testing

using Amazon.Controllers;
using Amazon.Models;
using Amazon.Repository;
using FakeItEasy;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AmazonAPITest.Amazon_Controller_Merchant
{
    public  class Merchant_Controller_Test
    {
        private readonly IMerchantRepository _merchantRepository;

        public Merchant_Controller_Test()
        {
            _merchantRepository = A.Fake<IMerchantRepository>();
            
        }
        [Fact]
        public void MerchantController_GetMerchants_ListMerchantAsync()
        {
            //Arrange
            var MerchantList = A.Fake<Task<List<Merchant>>>();
            A.CallTo(() => _merchantRepository.GetMerchant()).Returns(MerchantList);
            var MerchantController = new MerchantController(_merchantRepository);
            var expected = A.Fake<Task<ActionResult<List<Merchant>>>>();

            //Act
            var result=  MerchantController.GetMerchants();
            
            //Assert
            result.Should().NotBeNull();
            result.Should().BeOfType<Task<ActionResult<List<Merchant>>>>();
           
            
        }
    }
}

controller action method code

[HttpGet]
    public async Task<ActionResult<List<Merchant>>> GetMerchants()
    {
        return await _repository.GetMerchant();
    }
    

repository code

  public async Task<List<Merchant>> GetMerchant()
    {
        try
        {
            return await _context.Merchants.ToListAsync();
        }
        catch
        {
            throw new NotImplementedException();

        }
    }

exception on debugging the test

Xunit.Sdk.XunitException: 'Expected type to be System.Threading.Tasks.Task1[[Microsoft.AspNetCore.Mvc.ActionResult1[[System.Collections.Generic.List1[[Amazon.Models.Merchant, AmazonAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Mvc.Core, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]]**, but found **System.Runtime.CompilerServices.AsyncTaskMethodBuilder1+AsyncStateMachineBox1[[Microsoft.AspNetCore.Mvc.ActionResult1[[System.Collections.Generic.List`1[[Amazon.Models.Merchant, AmazonAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.AspNetCore.Mvc.Core, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Runtime.CompilerServices.IAsyncStateMachine, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].

can someone figure any way to fake async method with fake it easy

Upvotes: 1

Views: 875

Answers (1)

Blair Conrad
Blair Conrad

Reputation: 241714

The problem isn't in faking the async method. It's how you've defined the return value:

var MerchantList = A.Fake<Task<List<Merchant>>>();

A fake Task<List<Merchant>> is not a Task<ActionResult<List<Merchant>>>.

When I replace your definition with

var MerchantList = new List<Merchant>();

(and supply a definition for MerchantController) the test passes. It's also easier to understand: an empty list is more familiar to the reader than a faked list. I'd recommend using concrete objects for your return values whenever possible.

(Also note that expected is unused in your example.)

My updated code, based on your expanded example:

// using Amazon.Controllers;
// using Amazon.Models;
// using Amazon.Repository;
using FakeItEasy;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AmazonAPITest.Amazon_Controller_Merchant
{
    public interface IMerchantRepository
    {
        Task<List<Merchant>> GetMerchant();
    }

    public class Merchant
    {
    }

    public class MerchantController
    {
        private readonly IMerchantRepository _repository;

        public MerchantController(IMerchantRepository repository) => _repository = repository;

        public async Task<ActionResult<List<Merchant>>> GetMerchants() => await _repository.GetMerchant();
    }


    public class Merchant_Controller_Test
    {
        private readonly IMerchantRepository _merchantRepository;

        public Merchant_Controller_Test()
        {
            _merchantRepository = A.Fake<IMerchantRepository>();

        }
        [Fact]
        public void MerchantController_GetMerchants_ListMerchantAsync()
        {
            //Arrange

            // THIS IS THE ONLY CHANGE I MADE TO THE TEST
            // var MerchantList = A.Fake<Task<List<Merchant>>>();
            var MerchantList = new List<Merchant>();

            A.CallTo(() => _merchantRepository.GetMerchant()).Returns(MerchantList);
            var MerchantController = new MerchantController(_merchantRepository);
            var expected = A.Fake<Task<ActionResult<List<Merchant>>>>();

            //Act
            var result = MerchantController.GetMerchants();

            //Assert
            result.Should().NotBeNull();
            result.Should().BeOfType<Task<ActionResult<List<Merchant>>>>();
        }
    }
}

Aside from this change, I would also ask you to consider why you're checking the type of the return value. It's a sort of weak condition, while also being brittle. A stronger and probably less fragile test might use the returned value to see if it has expected properties, such as the right contents. But perhaps you've simplified for reproduction benefits.

Upvotes: 1

Related Questions