3
votes

Type de retour Moq IEnumerable renvoyé sous forme de liste

J'ai du mal à faire en sorte que Moq renvoie les types IEnumerable partir des méthodes de référentiel.

Mise à jour - 1

J'espérais qu'il me manquait quelque chose mais, comme demandé, voici la mise en œuvre complète qui est en place.

L'affirmation que l' objResult.Value est de type IEnumerable<ListItemDTO> est ce qui échoue.

Interface du référentiel

public class TARTrackerControllerTests
{
    public static ILogger<TARTrackerController> Logger = Mock.Of<ILogger<TARTrackerController>>();
    public static Mock<ITARRepository> Repository = new Mock<ITARRepository>();

    public class GetTARTrackersTests
    {
        [Fct]
        public async Task Returns_OK_With_ListItemDTO()
        {
            //arrange
            var id = 12345;
            IEnumerable<ListItemDTO> expected = new List<ListItemDTO>();

            Repository
                .Setup(repo => repo.GetListAsync<ListItemDTO>(It.IsAny<string>(), It.IsAny<object>()))
                .ReturnsAsync(expected);

            var controller = new TARTrackerController(Logger, Repository.Object);

            //act
            var result = await controller.Get(id);

            //assert
            var objResult = Assert.IsType<OkObjectResult>(result);
            Assert.Equal(typeof(IEnumerable<ListItemDTO>), objResult.Value.GetType());
        }
    }
}

Manette

public class TARTrackerController : Controller
{
    private readonly ITARRepository Repository;
    private readonly ILogger<TARTrackerController> Logger;

    public TARTrackerController(ILogger<TARTrackerController> logger, ITARRepository repository) 
    {
        Repository = repository;
        Logger = logger;
    }

    [HttpGet("TARTrackers")]
    [SwaggerResponse(HttpStatusCode.OK, typeof(IEnumerable<ListItemDTO>))]
    [SwaggerResponse(HttpStatusCode.Unauthorized, null)]
    [SwaggerResponse(HttpStatusCode.InternalServerError, null)]
    [SwaggerResponse(HttpStatusCode.ServiceUnavailable, null)]
    [Description("Get TAR Tracker List")]
    public async Task<IActionResult> Get(long accountId)
    {
        try
        {
            var trackers = await Repository.GetListAsync<ListItemDTO>("spFDBGetAccountTARList", new { @ParamAccountID = accountId });

            return Ok(trackers);
        }
        catch (Exception e)
        {
            Logger.LogError(LogEvents.Services, e, $"An error occured in {0}.{1}", nameof(TARTrackerController), nameof(Get));

            return StatusCode((int)HttpStatusCode.InternalServerError);
        }
    }
}

Test de l'unité:

public interface IRepository
{
    Task<IEnumerable<T>> GetListAsync<T>(string storedProcedure, object template);

    Task<IEnumerable<T>> GetListAsync<T>(string sql, CommandType commandType, object template);
}

public interface ICRUDRepository<TEntity>
{
    Task<TEntity> GetByIdAsync(long Id);

    Task<int> AddAsync(TEntity entity);

    Task<int> UpdateAsync(TEntity entity);

    Task<int> DeleteAsync(long id);
}

public interface ITARRepository : IRepository, ICRUDRepository<TARTracker>
{
    Task<IEnumerable<TARTrackerDate>> GetTARTrackerApprovedDates(long tarTrackerId);
    Task<IEnumerable<TARTrackerDate>> MergeTARApprovalDateChanges(DataTable approvedDates);
}

Je pense que cela devrait correspondre. Je n'arrive pas à comprendre ce qui se passe. De l'aide?


7 commentaires

Avez-vous essayé expected.AsEnumerable() ?


@MuhammadHannan ouais, il renvoie toujours List<T> .


@dbarth Ce qui se passe réellement en rapport avec ce que vous prévoyez d'arriver.


@dbarth Ce serait génial si vous pouviez partager un exemple minimal reproductible du test unitaire qui peut être utilisé pour reproduire le problème. Ce qui est montré jusqu'à présent devrait fonctionner, mais avec une image complète de ce qui est testé et qui se passe réellement, nous nous retrouvons à deviner le problème.


Je soupçonne que vous vous attendez à expected.GetType() que expected.GetType() renvoie IEnumerable - pourriez-vous s'il vous plaît éditer le message pour clarifier si c'est le cas ou maintenant (ainsi que la mise à jour de l'exemple de code + résultat attendu comme @Nkosi l'a suggéré)


@Nkosi J'ai mis à jour l'implémentation et ce qui est renvoyé par le référentiel n'est pas clair lorsque j'essaye de déboguer le test. Cependant, lorsqu'il est renvoyé par le contrôleur, il s'agit d'un type de List<ListItemDTO> .


Vous souciez-vous vraiment du type de valeur réelle? Ce qui vous importe, c'est que la valeur retournée peut être énumérée et contenir des valeurs attendues.


3 Réponses :


-1
votes

Remplacement

IEnumerable<ListItemDTO> expected = new List<ListItemDTO>();

avec

IEnumerable<ListItemDTO> expected = Enumerable.Empty<ListItemDTO>

devrait marcher.

Cependant, je ne suis pas sûr que ce soit un problème dans tous les cas; parce que List<T> est déjà une implémentation de IEnumerable<T> , donc d'un point de vue "typage", c'est tout à fait OK; et toute vérification que vous vouliez faire sur le contenu des données renvoyées, etc., fonctionnera aussi bien si le type est List<T> que si c'est IEnumerable<T>


1 commentaires

@tymtam - car il renvoie un IEnumerable<T> qui serait une correspondance de type exacte. Gardez à l'esprit que la question telle que posée à l'origine ne précise pas vraiment quel est le problème (pas de détails sur l'échec du test, etc.), donc c'était un petit coup de feu dans le noir avec la réponse ci-dessus.



0
votes

Il y a une faute d'orthographe dans IEnumerable dans ce que vous affichez comme code du contrôleur, ce qui suggère que ce n'est pas le code que vous utilisez vraiment. Cela peut signifier ou non que l'erreur n'est pas liée au code de test.

Je ne sais pas où se trouve le problème dans votre code, mais voici un test de réussite.

<package id="Moq" version="4.13.1" targetFramework="net472" />
<package id="MSTest.TestAdapter" version="2.0.0" targetFramework="net472" />
<package id="MSTest.TestFramework" version="2.0.0" targetFramework="net472" />

La configuration de la bibliothèque est la suivante:

public class ListItemDTO { public string Name { get; set; } }

public interface IRepo
{
    Task<IEnumerable<T>> GetListAsync<T>(string storedProcedure, object template);
}

public class Ctrl
{
    private readonly IRepo repo;
    public Ctrl(IRepo repo) { this.repo = repo; }

    // BTW. Check out IAsyncEnumerable<T> 
    public async Task<IEnumerable<ListItemDTO>> GetAync()
        => await repo.GetListAsync<ListItemDTO>("spGetAccountList", new { @ParamAccountID = 1 });
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public async Task Test1()
    {
        var mockRepo = new Mock<IRepo>(MockBehavior.Strict);

        IEnumerable<ListItemDTO> expected = new List<ListItemDTO>() { new ListItemDTO { Name = "Prince" } };

        mockRepo.Setup(repo => repo.GetListAsync<ListItemDTO>(It.IsAny<string>(), It.IsAny<object>()))
                .ReturnsAsync(expected);

        var tested = new Ctrl(repo: mockRepo.Object);

        var actual = await tested.GetAync();

        Assert.IsNotNull(actual);
        Assert.AreEqual(1, actual.Count(), "Expecting exactly one item");
        var first = actual.First();
        Assert.AreEqual("Prince", first.Name, "Checking 1st item's 'Name'");
    }
}


0 commentaires

1
votes

Donc, il semble que seule l'affirmation était fausse, ce qui était le consensus général ici, mais elle devrait en fait être accomplie de la manière suivante.

Assert.IsAssignableFrom<IEnumerable<ListItemDTO>>(objResult.Value);


0 commentaires