Прочитайте значения параметров json приложения в.NET Core Test Project

Моему веб-приложению нужно прочитать ключи Document DB из файла appsettings.json. Я создал класс с именами ключей и читал раздел Config в ConfigureaServices() как:

    public Startup(IHostingEnvironment env) {
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services) {
        services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
        services.AddSession();
        Helpers.GetConfigurationSettings(services, Configuration);
        DIBuilder.AddDependency(services, Configuration);
    }

Я ищу способы прочитать значения ключа в тестовом проекте.

4 ответа

Это основано на публикации в блоге " Использование файлов конфигурации в проектах модульного тестирования.NET Core" (написано для.NET Core 1.0).

  1. Создайте (или скопируйте) файл appsettings.test.json в корневом каталоге тестового проекта интеграции и в свойствах укажите "Создать действие" в качестве содержимого и "Скопировать, если новее" в выходной каталог. Обратите внимание, что лучше иметь имя файла (например, appsettings.test.json) отличается от нормального appsettings.json, поскольку возможно, что файл из основного проекта переопределит файл из тестового проекта, если будет использоваться то же имя.

  2. Включите пакет NuGet файла конфигурации JSON (Microsoft.Extensions.Configuration.Json), если он еще не включен.

  3. В тестовом проекте создайте метод,

    public static IConfiguration InitConfiguration()
            {
                var config = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.test.json")
                    .Build();
                    return config;
            }
    
  4. Используйте конфигурацию как обычно

    var config = InitConfiguration();
    var clientId = config["CLIENT_ID"]
    

Кстати, вам также может быть интересно прочитать конфигурацию в классе IOptions, как описано в тесте интеграции с IOptions<> в.NET Core:

var options = config.Get<MySettings>();

Добавьте файл конфигурации

Сначала добавьте файл appconfig.json в тестовый проект интеграции.

Настройте файл appconfig.json для копирования в выходной каталог, обновив

Добавить пакет NuGet

  • Microsoft.Extensions.Configuration.Json

Используйте конфигурацию в своих модульных тестах

[TestClass]
public class IntegrationTests
{
    public IntegrationTests()
    {
        var config = new ConfigurationBuilder().AddJsonFile("appconfig.json").Build();
        
        _numberOfPumps = Convert.ToInt32(config["NumberOfPumps"]);

        _numberOfMessages = Convert.ToInt32(config["NumberOfMessages"]);

        _databaseUrl = config["DatabaseUrlAddress"];
    }
} 

Решение Садерсона сработало для меня, когда изменилось, как показано ниже:

        var builder = new ConfigurationBuilder()
             .SetBasePath(Directory.GetCurrentDirectory())
             .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
             .AddEnvironmentVariables();

        IConfiguration config = builder.Build();

        //Now, You can use config.GetSection(key) to get the config entries

Для проектов ASP.NET Core 2.x скопируйте appsettings.json автоматически подать в каталог сборки:

<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <None Include="..\MyProj\appsettings.json" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>
</Project>

Скопируйте appSettings.json в корневой каталог вашего тестового проекта и отметьте его свойство как Content and Copy, если оно более новое.

var builder = new ConfigurationBuilder()
  .SetBasePath(Directory.GetCurrentDirectory())
  .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
  .AddEnvironmentVariables();
ConfigurationManager.Configuration = builder.Build();

ConfigurationManager это класс, и он имеет статическое свойство Configuration, Таким образом, все приложение может просто получить к нему доступ как ConfigurationManager.Configuration[<key>]

Аналогично ответу Артема, но с использованием потока:

Stream configStream =
    Assembly.GetExecutingAssembly()
    .GetManifestResourceStream("MyNamespace.AppName.Test.appsettings.test.json");

IConfigurationRoot config = new ConfigurationBuilder()
    .AddJsonStream(configStream)
    .AddEnvironmentVariables()
    .Build();

Я предпочитаю читать конфигурацию из потока, а не из файла. Это дает большую гибкость, потому что вы можете создать легкую тестовую установку без фиксации нескольких файлов конфигурации json:

public static class ConfigurationHelper
{
    public static IConfigurationRoot GetConfiguration()
    {
        byte[] byteArray = Encoding.ASCII.GetBytes("{\"Root\":{\"Section\": { ... }}");
        using var stream = new MemoryStream(byteArray);
        return new ConfigurationBuilder()
            .AddJsonStream(stream)
            .Build();
    }
}

В project.json из вашего тестового проекта добавьте следующие зависимости:

"dependencies": {
  "xunit": "2.2.0-beta2-build3300",
  "Microsoft.AspNetCore.TestHost": "1.0.0",
  "dotnet-test-xunit": "2.2.0-preview2-build1029",
  "BancoSentencas": "1.0.0-*"
},

BancoSentencas это проект, который я хочу проверить. Другие пакеты от xUnit и TestHost, которые будут нашим сервером в памяти.

Включите также эту опцию сборки для appsettings.json:

"buildOptions": {
  "copyToOutput": {
    "include": [ "appsettings.Development.json" ]
  }
}

В моем тестовом проекте у меня есть следующий тестовый класс:

  public class ClasseControllerTeste : IClassFixture<TestServerFixture> {

    public ClasseControllerTeste(TestServerFixture fixture) {
      Fixture = fixture;
    }

    protected TestServerFixture Fixture { get; private set; }


    [Fact]
    public async void TestarRecuperarClassePorId() {
      using(var client = Fixture.Client) {
        var request = await Fixture.MyHttpRequestMessage(HttpMethod.Get, "/api/classe/1436");
        var response = await client.SendAsync(request);
        string obj = await response.Content.ReadAsStringAsync();
        ClasseModel classe = JsonConvert.DeserializeObject<ClasseModel>(obj);
        Assert.NotNull(classe);
        Assert.Equal(1436, classe.Id);
      }
    }
  }

И у меня также есть класс TestServerFixture, который будет настраивать сервер в памяти:

  public class TestServerFixture : IDisposable {
    private TestServer testServer;
    protected TestServer TestServer {
      get {
        if (testServer == null)
          testServer = new TestServer(new WebHostBuilder().UseEnvironment("Development").UseStartup<Startup>());
        return testServer;
      }
    }

    protected SetCookieHeaderValue Cookie { get; set; }

    public HttpClient Client {
      get {
        return TestServer.CreateClient();
      }
    }

    public async Task<HttpRequestMessage> MyHttpRequestMessage(HttpMethod method, string requestUri) {      
      ...
      login stuff...
      ...
      Cookie = SetCookieHeaderValue.Parse(response.Headers.GetValues("Set-Cookie").First());

      var request = new HttpRequestMessage(method, requestUri);

      request.Headers.Add("Cookie", new CookieHeaderValue(Cookie.Name, Cookie.Value).ToString());
      request.Headers.Accept.ParseAdd("text/xml");
      request.Headers.AcceptCharset.ParseAdd("utf-8");
      return request;
    }

    public void Dispose() {
      if (testServer != null) {
        testServer.Dispose();
        testServer = null;
      }
    }
  }

Вот так я тестирую свой проект. Я использую Startup.cs из основного проекта и создаю копию из appsettings.json в своем тестовом проекте (appsettings.Development.json)

Честно говоря, если вы проводите модульное тестирование приложения, вы должны попытаться изолировать класс, который вы тестируете, от всех зависимостей, таких как вызов других классов, доступ к файловой системе, базе данных, сети и т. Д., Если вы не проводите интеграционное тестирование или функциональное тестирование.

При этом для модульного тестирования приложения вы, вероятно, захотите смоделировать эти значения из файла appsettings.json и просто проверить свою логику.

Так что ваши appsettings.json будет выглядеть так

"DocumentDb": {
    "Key": "key1" 
} 

Затем создайте класс настроек.

public class DocumentDbSettings
{
    public string Key { get; set; }
}

Затем зарегистрируйте его в ConfigureServices() метод.

services.Configure<DocumentDbSettings>(Configuration.GetSection("DocumentDb"));

Тогда, например, ваш контроллер / класс может выглядеть следующим образом.

// ...
private readonly DocumentDbSettings _settings;

public HomeController(IOptions<DocumentDbSettings> settings)
{
    _settings = settings.Value;
}
// ...
public string TestMe()
{
    return $"processed_{_settings.Key}";
}

Затем в своем проекте тестов вы можете создать такой класс модульных тестов.

public class HomeControllerTests
{
    [Fact]
    public void TestMe_KeyShouldBeEqual_WhenKeyIsKey1()
    {
        // Arrange
        const string expectedValue = "processed_key1";
        var configMock = Substitute.For<IOptions<DocumentDbSettings>>();
        configMock.Value.Returns(new DocumentDbSettings
        {
            Key = "key1" // Mocking the value from your config
        });

        var c = new HomeController(configMock);

        // Act
        var result = c.TestMe();

        // Assert
        Assert.Equal(expectedValue, result);
    }
}

Я использовал NSubstitute v2.0.0-rc для насмешек.

Если вы используете WebApplicationFactory чтобы создать тестовый сервер для интеграционных тестов, и у вас уже есть способ получить значения конфигурации в контроллерах на стороне сервера (вы, вероятно, делаете!), тогда вы можете просто повторно использовать это (и получить любые другие внедренные элементы, которые вам нужны) в ваших интеграционных тестах следующим образом:

// Your test fixtures would be subclasses of this
public class IntegrationTestBase : IDisposable
{
    private readonly WebApplicationFactory<Startup> _factory;
    protected readonly HttpClient _client;

    // The same config class which would be injected into your server-side controllers
    protected readonly IMyConfigService _myConfigService;

    // Constructor (called by subclasses)
    protected IntegrationTestBase()
    {
        // this can refer to the actual live Startup class!
        _factory = new WebApplicationFactory<Startup>();
        _client = _factory.CreateClient();

        // fetch some useful objects from the injection service
        _myConfigService = (IMyConfigService)_factory.Server.Host.Services.GetService(typeof(IMyConfigService));
    }

    public virtual void Dispose()
    {
        _client.Dispose();
        _factory.Dispose();
    }
}

Обратите внимание, что вам не нужно копировать appsettings.json в этом случае вы автоматически используете тот же appsettings.json который использует (тестовый) сервер.

Другие вопросы по тегам