How can I use IConfiguration from my integration tests?
Asked Answered
D

2

16

I have an API, and I'm trying to make some integration tests for it with XUnit. Here's my API controller constructor:

public class MyController : Controller
{
    readonly IMyRepository _myRepository;

    public MyController(IMyRepository myRepository)
    {
        _myRepository = myRepository;
    }

    public async Task<IActionResult> GetUser(Guid userId)
    {
        try
        {
            return Ok(await _my.GetUser(userId));
        }
        catch (Exception ex)
        {
            return StatusCode(500, "An error occurred while handling your request.");
        }
    }
}

My API controller is using this repository:

public class MyRepository : IMyRepository
{
    private string _connectionString;

    public MyRepository(IConfiguration config)
    {
        _connectionString = config.GetConnectionString("DefaultConnection");
    }


    public async Task<User> GetUser(Guid userId)
    {
        using (SqlConnection con = new SqlConnection(connectionString))
        {
            // call stored proc
        }
    }
}

The repository uses the connection string to then make some database calls in my methods. This works when I'm calling my API methods since the configuration was setup in the Startup class of my API application.

But I'm not sure how to pass a configuration object with the connection string to my repository from my integration test methods:

public async Task GetUserShouldReturnOk()
{
    var userId = new Guid();
    var configuration = // ????

    var controller = new MyController(
        new MyRepository(configuration));

    var result = await controller.GetUser(userId);

    Assert.IsType<OkResult>(result);
}

I've tried adding a json settings file to my XUnit project with my connection string info, and then trying to build it like my Startup class does, but I get an error because it's looking for the json file in my test project's \bin\Debug\netcoreapp2.0\ directory, so I'm not sure how to point the base path to the correct place either:

var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

Is there a proper way to do this?

Distant answered 17/9, 2017 at 23:56 Comment(5)
Will the repository be connecting to an actual data source or will the source be mocked with fake data? Would simply mock the configuration interface and inject it into the dependent class. There is not enough information to provide anything better than what was just suggested.Stimulus
Could you show how the connection string will be used. I believe there may be some design issues with the current repository implementation.Stimulus
@Stimulus - it's connecting to an actual data source. I've added code so you can see how the connection string is being used in the repository.Distant
After thinking my original suggestion over, why not just set the base path of the config builder to the path of where the json file is?Stimulus
You should also check out the official documentation on the subject Integration testing in ASP.NET CoreStimulus
S
13

The appsettings.json file is just in my test project root, do you know an easy way to get the current project path so that I don't have to hard-code that value

Set the Build Action property of the file to Content so it will copy to output directory so it is moved to the bin when testing and then you can use the original config code with the .SetBasePath(Directory.GetCurrentDirectory())

public async Task GetUserShouldReturnOk() {
    var userId = new Guid();
    var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

    var controller = new MyController(
        new MyRepository(configuration));

    var result = await controller.GetUser(userId);

    Assert.IsType<OkResult>(result);
}
Stimulus answered 18/9, 2017 at 0:56 Comment(2)
Thanks for the answer @Nkosi. The appsettings.json file is just in my test project root, do you know an easy way to get the current project path so that I don't have to hardcode that value?Distant
@Steven, Well you can change the properties of the file to copy to output directory so it is moved to the bin when testing and then you can use the original config code with the .SetBasePath(Directory.GetCurrentDirectory())Stimulus
N
19

I think it's better to use dependency injection in your test project; for future use.

In your test project:

  1. Add a new appsettings.json file to your test project with all the configurations you need.
  2. Install the package: Microsoft.Extensions.DependencyInjection.
  3. Create a class for the test setup; for instance TestSetup.cs.

    public class TestSetup
    {
        public TestSetup()
        {
            var serviceCollection = new ServiceCollection();
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile(
                     path: "appsettings.json",
                     optional: false,
                     reloadOnChange: true)
               .Build();
            serviceCollection.AddSingleton<IConfiguration>(configuration);
            serviceCollection.AddTransient<MyController, MyController>();
    
            ServiceProvider = serviceCollection.BuildServiceProvider();
        }
    
        public ServiceProvider ServiceProvider { get; private set; }
    }
    
  4. In your test class; I am using Xunit

    public class MyControllerTest: IClassFixture<TestSetup>
    {
        private ServiceProvider _serviceProvider;
        private MyController _myController;
    
        public MyControllerTest(TestSetup testSetup)
        {
           _serviceProvider = testSetup.ServiceProvider;
           _myController = _serviceProvider.GetService<MyController>();
        }
    
        [Fact]
        public async Task GetUserShouldReturnOk()
        {
            var result = await _myController.GetUser(userId);
            Assert.IsType<OkResult>(result);
        }
    
    }
    
Nieves answered 13/9, 2019 at 1:15 Comment(0)
S
13

The appsettings.json file is just in my test project root, do you know an easy way to get the current project path so that I don't have to hard-code that value

Set the Build Action property of the file to Content so it will copy to output directory so it is moved to the bin when testing and then you can use the original config code with the .SetBasePath(Directory.GetCurrentDirectory())

public async Task GetUserShouldReturnOk() {
    var userId = new Guid();
    var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json")
            .Build();

    var controller = new MyController(
        new MyRepository(configuration));

    var result = await controller.GetUser(userId);

    Assert.IsType<OkResult>(result);
}
Stimulus answered 18/9, 2017 at 0:56 Comment(2)
Thanks for the answer @Nkosi. The appsettings.json file is just in my test project root, do you know an easy way to get the current project path so that I don't have to hardcode that value?Distant
@Steven, Well you can change the properties of the file to copy to output directory so it is moved to the bin when testing and then you can use the original config code with the .SetBasePath(Directory.GetCurrentDirectory())Stimulus

© 2022 - 2024 — McMap. All rights reserved.