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

Posted

技术标签:

【中文标题】在 .NET Core 测试项目中读取 appsettings json 值【英文标题】:Read appsettings json values in .NET Core Test Project 【发布时间】:2017-02-09 00:55:41 【问题描述】:

我的 Web 应用程序需要从 appsettings.json 文件中读取 Document DB 密钥。我创建了一个带有键名的类,并将 ConfigureServices() 中的 Config 部分读取为:

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

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

【问题讨论】:

docs.asp.net/en/latest/fundamentals/… 【参考方案1】:

这是基于博客文章 Using Configuration files in .NET Core Unit Test Projects(为 .NET Core 1.0 编写的)。

    在集成测试项目根目录中创建(或复制)appsettings.test.json,并在属性中将“构建操作”指定为内容,将“如果较新则复制”到输出目录。请注意,文件名(例如 appsettings.test.json )最好与普通的 appsettings.json 不同,因为如果使用相同的名称,主项目中的文件可能会覆盖测试项目中的文件。

    如果尚未包含 JSON 配置文件 NuGet 包 (Microsoft.Extensions.Configuration.Json),请包含它。

    在测试项目中创建一个方法,

     public static IConfiguration InitConfiguration()
             
                 var config = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.test.json")
                     .AddEnvironmentVariables() 
                     .Build();
                     return config;
             
    

AddEnvironmentVariables(建议在 @RickStrahl blog 中)如果你想传递一些你不希望存储在 appsettings.test.json 中的秘密很有用

    照常使用配置

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

顺便说一句:您也可能对将配置读入 IOptions 类感兴趣,如 Integration test with IOptions<> in .NET Core 中所述:

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

【讨论】:

config.Get() 返回空值。你应该像这样使用 IOptions; ***.com/questions/46019988/…【参考方案2】:

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

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

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

【讨论】:

一张很棒的照片,在 3 小时的挫折后帮助了我。【参考方案3】:

添加配置文件

首先,将 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"];
    
 

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

如果您正在使用 WebApplicationFactory to create a test server for integration tests 并且您已经有一种方法可以获取服务器端控制器中的配置值(您可能会这样做!),那么您可以重新使用它(并获取任何您需要的其他注入项目)在您的集成测试中,如下所示:

// 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

【讨论】:

嗨,迈克,我正在使用您建议的相同方法。但是我必须覆盖一些设置,我找不到办法做到这一点。有什么建议吗? 嗨,这确实有道理。我只需要我的集成测试设置与我的开发设置相同。我认为appsettings.json 只支持开发、生产和登台,所以如果您需要第四个变体进行测试,我不确定。我怀疑会有一种方法可以注入一些额外的配置(因为我认为所有配置都是按顺序搜索的),这会覆盖你所需要的。【参考方案7】:

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

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

【讨论】:

这行得通,而且 VS 足够聪明,知道它是同一个文件。当然,您对“测试”版本所做的任何编辑都将复制到服务器版本中,因为它是同一个文件。【参考方案8】:

appSettings.json 复制到您的测试项目根目录并将其属性标记为Content如果较新则复制

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

ConfigurationManager 是一个类,它有一个静态属性Configuration。这样整个应用程序就可以以ConfigurationManager.Configuration[&lt;key&gt;] 的身份访问它

【讨论】:

前半句是对的。使用静态 ConfigurationManager.Configuration 听起来不正确。【参考方案9】:

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

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

【讨论】:

是的,但是...如果我在做集成测试怎么办?您完全没有回答实际问题【参考方案10】:

在您的测试项目的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是什么?你的自定义类? 这是来自Microsoft.AspNetCore.TestHost 包的类。你在使用 xUnit 吗?我将编辑我的答案并提供更多详细信息。 是的。我也在使用 xUnit。 感谢详细的代码。我的应用程序不是 Web API。那么请帮助我如何测试它? 那么,你的应用是一个 MVC,对吧?你想测试你的 MVC 控制器吗?

以上是关于在 .NET Core 测试项目中读取 appsettings json 值的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET Core 2.2 中使用 IValidateOptions 验证配置设置?

如何在 ASP.NET Core 项目中读取本地文件?

[Asp.Net Core] 命令行参数读取+配置多种读取

[Asp.Net Core] 命令行参数读取+配置多种读取

.NET Core类库项目中如何读取appsettings.json中的配置

在测试项目中使用 ASP.NET Core 的 ConfigurationBuilder