302 votes

Test unitaire .Net Core - Mock IOptions<T>

J'ai l'impression de passer à côté de quelque chose de vraiment évident. J'ai des classes qui nécessitent l'injection d'options à l'aide du modèle IOptions de .Net Core ( ?). Lorsque je vais tester cette classe, je veux simuler différentes versions des options pour valider la fonctionnalité de la classe. Quelqu'un sait-il comment simuler/instaurer/populer correctement les IOptions en dehors de la classe Startup ?

Voici quelques exemples de classes avec lesquelles je travaille :

Paramètres/Options Modèle

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace OptionsSample.Models
{
    public class SampleOptions
    {
        public string FirstSetting { get; set; }
        public int SecondSetting { get; set; }
    }
}

Classe à tester qui utilise les paramètres :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OptionsSample.Models
using System.Net.Http;
using Microsoft.Extensions.Options;
using System.IO;
using Microsoft.AspNetCore.Http;
using System.Xml.Linq;
using Newtonsoft.Json;
using System.Dynamic;
using Microsoft.Extensions.Logging;

namespace OptionsSample.Repositories
{
    public class SampleRepo : ISampleRepo
    {
        private SampleOptions _options;
        private ILogger<AzureStorageQueuePassthru> _logger;

        public SampleRepo(IOptions<SampleOptions> options)
        {
            _options = options.Value;
        }

        public async Task Get()
        {
        }
    }
}

Test unitaire dans un assemblage différent des autres classes :

using OptionsSample.Repositories;
using OptionsSample.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;

namespace OptionsSample.Repositories.Tests
{
    public class SampleRepoTests
    {
        private IOptions<SampleOptions> _options;
        private SampleRepo _sampleRepo;

        public SampleRepoTests()
        {
            //Not sure how to populate IOptions<SampleOptions> here
            _options = options;

            _sampleRepo = new SampleRepo(_options);
        }
    }
}

0 votes

Confondez-vous le sens du mot "moquerie" ? Vous vous moquez d'une interface et la configurez pour qu'elle renvoie une valeur spécifiée. Pour IOptions<T> il suffit de se moquer Value pour retourner la classe que vous désirez

2voto

Mithrandir Points 61

Pour mes tests système et d'intégration, je préfère avoir une copie/lien de mon fichier de configuration dans le projet de test. J'utilise ensuite le ConfigurationBuilder pour obtenir les options.

using System.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace SomeProject.Test
{
public static class TestEnvironment
{
    private static object configLock = new object();

    public static ServiceProvider ServiceProvider { get; private set; }
    public static T GetOption<T>()
    {
        lock (configLock)
        {
            if (ServiceProvider != null) return (T)ServiceProvider.GetServices(typeof(T)).First();

            var builder = new ConfigurationBuilder()
                .AddJsonFile("config/appsettings.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables();
            var configuration = builder.Build();
            var services = new ServiceCollection();
            services.AddOptions();

            services.Configure<ProductOptions>(configuration.GetSection("Products"));
            services.Configure<MonitoringOptions>(configuration.GetSection("Monitoring"));
            services.Configure<WcfServiceOptions>(configuration.GetSection("Services"));
            ServiceProvider = services.BuildServiceProvider();
            return (T)ServiceProvider.GetServices(typeof(T)).First();
        }
    }
}
}

De cette façon, je peux utiliser la configuration partout dans mon TestProject. Pour les tests unitaires, je préfère utiliser MOQ comme patvin80 l'a décrit.

0voto

Je suis d'accord avec Aleha pour dire que l'utilisation d'un fichier de configuration testSettings.json est probablement préférable. Et puis, au lieu d'injecter l'IOption, vous pouvez simplement injecter le vrai SampleOptions dans le constructeur de votre classe, lorsque vous testez la classe à l'unité, vous pouvez faire ce qui suit dans un fixture ou encore simplement dans le constructeur de la classe de test :

   var builder = new ConfigurationBuilder()
  .AddJsonFile("testSettings.json", true, true)
  .AddEnvironmentVariables();

  var configurationRoot = builder.Build();
  configurationRoot.GetSection("SampleRepo").Bind(_sampleRepo);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X