Чтение значений jset-значений appsettings в .NET Core Test Project

Моему веб-приложению необходимо прочитать ключи документа 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);
    }

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

Ответ 1

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

  1. Создайте (или скопируйте) appsettings.test.json в корневом каталоге тестового проекта интеграции, а в свойствах укажите "Build Action" в качестве содержимого и "Копировать, если новее" в выходной каталог. Обратите внимание, что лучше иметь имя файла (например, 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>();

Ответ 2

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

Сказав, что в unit test приложение, вы, вероятно, захотите высмеять эти значения из своего файла 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}";
}

Затем в проекте тестов вы можете создать такой класс unit test.

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 для насмешек.

Ответ 3

В 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)

Ответ 4

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

        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

Ответ 5

Скопируйте 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>]

Ответ 6

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

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

Ответ 7

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

// Your test fixtures would be subclasses of this
public class IntegrationTestBase
{
    // 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));
    }
}

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