2

We are creating an Integration Unit test (Xunit), which is calling the Real Application Startup.cs.

For some reason, Real project can read Configuration file/property correctly, however running it from the Integration test, it cannot read it. Its not placing anything into Configuration (conf) variable below. How would I resolve this? The reason is its not picking up is its not reading the internal dependency injection new from Net Core 2.2 which reads Configuration file. Trying to use .CreateDefaultBuilder Now.

IntegrationTest1.cs

        TestServer _server = new TestServer(new WebHostBuilder() .UseContentRoot("C:\\RealProject\\RealProject.WebAPI")
            .UseEnvironment("Development")
            .UseConfiguration(new ConfigurationBuilder()
            .SetBasePath("C:\\RealProject\\RealProject.WebAPI")
                .AddJsonFile("appsettings.json")
                .Build())
                .UseStartup<Startup>()
            .UseStartup<Startup>());

Real Project Startup.cs

    public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
    {
        Configuration = configuration;
        HostingEnvironment = hostingEnvironment;
    }

    public IHostingEnvironment HostingEnvironment { get; }
    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {

        var conf = Configuration;
        IConfiguration appConf = conf.GetSection("ConnectionStrings");
        var connstring = appConf.GetValue<string>("DatabaseConnection");

        services.AddDbContext<DbContext>(a => a.UseSqlServer(connstring));

Appsettings.Json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "ConnectionStrings": {
    "DatabaseConnection": "Data Source=.;Initial Catalog=ApplicationDatabase;Integrated Security=True"
  }
}

1 Answers1

0

What you want to do is create a Factory for your WebApplication which takes a Startup type. You can then use the IClassFixture interface to share the context of this factory with all of your tests in your test class.

How this looks in practice is:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup> where TStartup : class
{
    public CustomWebApplicationFactory() { }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder
            .ConfigureTestServices(
                services =>
                {
                    services.Configure(AzureADDefaults.OpenIdScheme, (System.Action<OpenIdConnectOptions>)(o =>
                    {
                        // CookieContainer doesn't allow cookies from other paths
                        o.CorrelationCookie.Path = "/";
                        o.NonceCookie.Path = "/";
                    }));
                }
            )
            .UseEnvironment("Production")
            .UseStartup<Startup>();
    }
}


public class AuthenticationTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
    private HttpClient _httpClient { get; }

    public AuthenticationTests(CustomWebApplicationFactory<Startup> fixture)
    {
        WebApplicationFactoryClientOptions webAppFactoryClientOptions = new WebApplicationFactoryClientOptions
        {
            // Disallow redirect so that we can check the following: Status code is redirect and redirect url is login url
            // As per https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#test-a-secure-endpoint
            AllowAutoRedirect = false
        };

        _httpClient = fixture.CreateClient(webAppFactoryClientOptions);
    }

    [Theory]
    [InlineData("/")]
    [InlineData("/Index")]
    [InlineData("/Error")]
    public async Task Get_PagesNotRequiringAuthenticationWithoutAuthentication_ReturnsSuccessCode(string url)
    {
        // Act
        HttpResponseMessage response = await _httpClient.GetAsync(url);

        // Assert
        response.EnsureSuccessStatusCode();
    }
}

I followed the guide here to get this working in my own code.

Matt Stannett
  • 2,700
  • 1
  • 15
  • 36
  • trying to implement more this style of answer, from John J https://stackoverflow.com/questions/41382978/appsettings-json-for-integration-test-in-asp-net-core –  Aug 02 '19 at 08:20
  • is there anyway to simplify and use this? var projectDir = System.IO.Directory.GetCurrentDirectory(); _server = new TestServer(new WebHostBuilder() .UseEnvironment("Development") .UseContentRoot(projectDir) .UseConfiguration(new ConfigurationBuilder() .SetBasePath(projectDir) .AddJsonFile("appsettings.json") .Build() ) .UseStartup()); https://stackoverflow.com/questions/41382978/appsettings-json-for-integration-test-in-asp-net-core –  Aug 02 '19 at 16:13
  • I have updated my answer, I use a method to do the trick for finding the `projectDir`. – John Jang Aug 05 '19 at 03:46
  • hi @MattStannett can you use setup Configuration in accordance to the actual question directory, thanks? I might sent bounty on question later, answer was not working new TestServer(new WebHostBuilder() .UseContentRoot("C:\\RealProject\\RealProject.WebAPI") .UseEnvironment("Development") .UseConfiguration(new ConfigurationBuilder() .SetBasePath("C:\\RealProject\\RealProject.WebAPI") .AddJsonFile("appsettings.json") .Build()) .UseStartup() .UseStartup()); –  Aug 06 '19 at 06:12
  • a more detailed question is here, which needs to be answered with full specifications, team has issue debugging/setting it up https://stackoverflow.com/questions/57331395/net-core-execute-all-dependency-injection-in-xunit-test-for-appservice-reposit Mohsen Esmailpour was talking just webfactory –  Aug 06 '19 at 06:13
  • You used `Startup` instead of `TStartup` in two rows. Is it a mistake? – Massimiliano Kraus May 01 '20 at 10:20
  • No, that is the convention for generics - where they type is specified on creation. – Matt Stannett May 01 '20 at 19:43