C# 读取.NET核心测试项目中的appsettings json值

C# 读取.NET核心测试项目中的appsettings json值,c#,unit-testing,asp.net-core,appsettings,C#,Unit Testing,Asp.net Core,Appsettings,我的Web应用程序需要从appsettings.json文件中读取文档DB密钥。我创建了一个具有键名的类,并将ConfigureServices()中的配置部分作为: public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json", optional: true, reloadOnChan

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

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

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

在测试项目的
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);
      }
    }
  }
公共类ClasseControllerTeste:IClassFixture{
公共类控制器测试(TestServerFixture){
夹具=夹具;
}
受保护的TestServerFixture Fixture{get;private set;}
[事实]
公共异步无效TestarRecuperarclassporid(){
使用(var client=Fixture.client){
var request=await Fixture.MyHttpRequestMessage(HttpMethod.Get,“/api/classe/1436”);
var response=wait client.sendaync(请求);
string obj=await response.Content.ReadAsStringAsync();
ClasseModel classe=JsonConvert.DeserializeObject(obj);
Assert.NotNull(classe);
断言相等(1436,类别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;
      }
    }
  }
公共类TestServerFixture:IDisposable{
专用测试服务器;
受保护的TestServer TestServer{
得到{
if(testServer==null)
testServer=新的testServer(新的WebHostBuilder().UseEnvironment(“Development”).UseStartup());
返回testServer;
}
}
受保护的SetCookieHeaderValue Cookie{get;set;}
公共HttpClient客户端{
得到{
返回TestServer.CreateClient();
}
}
公共异步任务MyHttpRequestMessage(HttpMethod方法,字符串请求URI){
...
登录资料。。。
...
Cookie=SetCookieHeaderValue.Parse(response.Headers.GetValues(“Set Cookie”).First());
var request=newhttprequestmessage(方法,requestUri);
添加(“Cookie”,新的CookieHeaderValue(Cookie.Name,Cookie.Value).ToString());
request.Headers.Accept.ParseAdd(“text/xml”);
request.Headers.AcceptCharset.ParseAdd(“utf-8”);
返回请求;
}
公共空间处置(){
if(testServer!=null){
Dispose();
testServer=null;
}
}
}
这就是我测试项目的方式。我使用主项目中的Startup.cs,从测试项目(appsettings.Development.json)中的appsettings.json创建一个副本。

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

话虽如此,要对应用程序进行单元测试,您可能需要模拟appsettings.json文件中的这些值,然后测试您的逻辑

因此,您的
appsettings.json
如下所示

"DocumentDb": {
    "Key": "key1" 
} 
// ...
private readonly DocumentDbSettings _settings;

public HomeController(IOptions<DocumentDbSettings> settings)
{
    _settings = settings.Value;
}
// ...
public string TestMe()
{
    return $"processed_{_settings.Key}";
}
然后创建一个设置类

public class DocumentDbSettings
{
    public string Key { get; set; }
}
然后在
ConfigureServices()
方法中注册它

services.Configure<DocumentDbSettings>(Configuration.GetSection("DocumentDb"));
services.Configure(Configuration.GetSection(“DocumentDb”);
例如,您的控制器/类可以如下所示

"DocumentDb": {
    "Key": "key1" 
} 
// ...
private readonly DocumentDbSettings _settings;

public HomeController(IOptions<DocumentDbSettings> settings)
{
    _settings = settings.Value;
}
// ...
public string TestMe()
{
    return $"processed_{_settings.Key}";
}
/。。。
私有只读文档设置;
公共家庭控制器(IOPS设置)
{
_设置=设置.值;
}
// ...
公共字符串TestMe()
{
返回$“已处理的{{u设置.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);
    }
}
公共类HomeControllerTests
{
[事实]
公共无效测试密钥应在密钥密钥1()时保持相等
{
//安排
const string expectedValue=“已处理的密钥1”;
var configMock=Substitute.For();
configMock.Value.Returns(新文档设置
{
Key=“key1”//模拟配置中的值
});
var c=新的HomeController(configMock);
//表演
var result=c.TestMe();
//断言
Assert.Equal(预期值、结果);
}
}

我使用NSubstitute v2.0.0-rc进行模拟。

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


ConfigurationManager
是一个类,它具有静态属性
Configuration
。这样整个应用程序就可以通过
ConfigurationManager.Configuration[]

访问它,这是基于博客文章(为.NET Core 1.0编写的)

  • 在集成测试项目根目录中创建(或复制)appsettings.test.json,并在属性中指定“Build Action”作为内容,“copy if newer”作为输出目录。请注意,最好使文件名(例如,
    appsettings.test.json
    )不同于正常的
    appsettings.json
    ,因为如果使用相同的名称,主项目中的文件可能会覆盖测试项目中的文件

  • 如果尚未包含JSON配置文件NuGet包(Microsoft.Extensions.Configuration.JSON),请将其包括在内

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

    public static IConfiguration InitConfiguration()
            {
                var config = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.test.json")
                    .Build();
                    return config;
            }
    
  • 像往常一样使用配置

    var config = InitConfiguration();
    var clientId = config["CLIENT_ID"]
    
  • 顺便说一句:您可能还对将配置读入IOptions类感兴趣,如中所述:

    var options=config.Get();
    
    当修改如下时,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
    

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

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

    如果您使用的是
    WebApplicationFactory
    ,并且您已经有了获取配置值的方法
    [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"];
        }
    } 
    
    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();
        }
    }
    
    Stream configStream =
        Assembly.GetExecutingAssembly()
        .GetManifestResourceStream("MyNamespace.AppName.Test.appsettings.test.json");
    
    IConfigurationRoot config = new ConfigurationBuilder()
        .AddJsonStream(configStream)
        .AddEnvironmentVariables()
        .Build();