在.NET Core测试项目中读取appsettings json值


74

我的Web应用程序需要从appsettings.json文件读取Document DB密钥。我创建了一个具有键名的类,并以如下方式读取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);
}

我正在寻找在测试项目中读取键值的方法。


Answers:


103

这是基于博客文章《在.NET Core单元测试项目中使用配置文件》(针对.NET Core 1.0编写)。

  1. 在Integration测试项目的根目录中创建(或复制)appsettings.test.json,并在属性中将“ Build Action”指定为Content,将“ Copy if newnew”复制到Output Directory。请注意,最好使用appsettings.test.json与正常名称不同的文件名(例如)appsettings.json,因为如果使用相同的名称,则主项目中的文件可能会覆盖测试项目中的文件。

  2. 如果尚未包含JSON配置文件NuGet包(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类中感兴趣,如在.NET Core中使用IOptions <>进行集成测试中所述:

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

1
config.Get <MySettings>()返回空值。您应该像这样使用IOptions。stackoverflow.com/questions/46019988/…–
卡亚

非常感谢
Sunil Dhappadhule

13

添加配置文件

首先,将一个appconfig.json文件添加到Integration测试项目中

通过更新将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"];
    }
} 

10

修改如下时,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

3

将复制appSettings.json到您的Test项目的根目录,并将其属性标记为Content如果更新则标记为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>]


3
上半年是对的。使用静态ConfigurationManager.Configuration听起来不正确。
Michael Freidgeim

2

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.Development.json)中从appsettings.json创建副本。


这是什么TestServer?您的自定义课程?
S.Siva

这是Microsoft.AspNetCore.TestHost包中的一类。您在使用xUnit吗?我将编辑我的答案并提供更多详细信息。
Fabricio Koch

是的 我也在使用xUnit。
S.Siva

感谢您提供详细的代码。我的应用程序不是Web API。那么请帮助我如何测试呢?
S.Siva

所以,您的应用是MVC,对吗?您要测试您的MVC控制器吗?
Fabricio Koch

1

我更喜欢从流而不是文件中读取配置。这提供了更大的灵活性,因为您可以创建轻量级的测试设置而无需提交多个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();
    }
}

1

类似于Artem答案,但使用嵌入式资源(作为流):

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

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

在此处输入图片说明


0

老实说,如果您正在对应用程序进行单元测试,则应尝试将测试的类与所有依赖项隔离开来,例如调用其他类,访问文件系统,数据库,网络等。除非您正在进行集成测试或功能测试。

话虽如此,要对应用程序进行单元测试,您可能想要从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进行模拟。


25
是的,但是...如果我正在进行集成测试该怎么办?您完全无法回答实际的问题
Joe Phillips

0

对于ASP.NET Core 2.x项目,将appsettings.json文件自动复制到生成目录:

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

这可以正常工作,而VS足够聪明,可以知道它是同一文件。当然,您对“测试”版本所做的任何编辑都会复制到服务器版本中,因为它是同一文件。
keithl8041

0

如果您正在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(测试)服务器使用的相同名称。


嗨,迈克,我使用的是您建议的相同方法。但是我必须重写一些设置,我找不到实现该设置的方法。有什么建议?
fkucuk

嗨,那确实有道理。我只需要集成测试设置与开发设置相同即可。我认为appsettings.json仅支持开发,生产和登台,因此如果您需要测试的第四个变体,我不确定。我怀疑是否有一种方法可以注入一些额外的配置(因为我认为所有配置都是按顺序搜索的),因此会覆盖您的需求。
MikeBeaton
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.