1

I am writing some integration tests for my web API, which means that it has to be running during the execution of the tests. Is there any way to run it with an in-memory database instead of a real one based on SQL Server?

Also, I need to run a few instances at a time, so I need somehow to change the base address of each of them to be unique. For example, I could append to the base URL these instance IDs, that are mentioned in the code below.

Here is the code which I am using to run a new instance for my tests:

public static class WebApiHelper
{
    private const string ExecutableFileExtension = "exe";

    private static readonly Dictionary<Guid, Process> _instances = new();

    public static void EnsureIsRunning(Assembly? assembly, Guid instanceId)
    {
        if (assembly is null)
            throw new ArgumentNullException(nameof(assembly));

        var executableFullName = Path.ChangeExtension(
            assembly.Location, ExecutableFileExtension);

        _instances.Add(instanceId, Process.Start(executableFullName));
    }

    public static void EnsureIsNotRunning(Guid instaceId)
        => _instances[instaceId].Kill();
}

Talking in general, is this a good way to create test instances, or maybe I am missing something? Asking this, because maybe there is another 'legal' way to achieve my goal.

11
  • What do you mean by in-memory database may I know which way you mean? Commented Aug 12, 2021 at 7:04
  • @MdFaridUddinKiron, I mean with the DbContext configured to use method UseInMemoryDatabase();. I know how to use it, but I need somehow to determine whether my API instance is running from tests or just a usual startup. Commented Aug 12, 2021 at 9:09
  • Yes I hope you can determine that on your startup.cs file as well. Commented Aug 12, 2021 at 9:25
  • You should be careful with InMemoryDatabase. It does not support SQL features like relation, stored procedure, etc. You can also use SQLite. Commented Aug 12, 2021 at 9:54
  • @İbrahimULUDAĞ, Thanks! I didn't know that. Then I will create a temp database and delete it when the tests are finished. Commented Aug 12, 2021 at 10:23

1 Answer 1

1

Okay, so in the end, I came up with this super easy and obvious solution.

As was mentioned in the comments - using the in-memory database is not the best way to test, because relational features are not supported if using MS SQL. So I decided to go another way.

Step 1: Overwrite the connection strings.

In my case, that was easy since I have a static IConfiguration instance and was need just to overwrite the connection string within that instance.

The method looks as follows:

private const string ConnectionStringsSectionName = "ConnectionStrings";

private const string TestConnectionStringFormat = "{0}_Test";

private static bool _connectionStringsOverwitten;

private static void OverwriteConnectionStrings()
{
    if (_connectionStringsOverwitten)
        return;

    var connectionStrings = MyStaticConfigurationContainer.Configuration
        .AsEnumerable()
        .Where(entry => entry.Key.StartsWith(ConnectionStringsSectionName)
                     && entry.Value is not null);

    foreach (var connectionString in connectionStrings)
    {
        var builder = new SqlConnectionStringBuilder(connectionString.Value);

        builder.InitialCatalog = string.Format(TestConnectionStringFormat,
                                               builder.InitialCatalog);

        MyStaticConfigurationContainer.Configuration[connectionString.Key] = builder.ConnectionString;
    }

    _connectionStringsOverwitten = true;
}

Of course, you would need to handle the database creation and deletion before and after running the tests, otherwise - your test DBs may become a mess.

Step 2: Simply run your web API instance within a separate thread.

In my case, I am using the NUnit test framework, which means I just need to implement the web API setup logic within the fixture. Basically, the process would be more or less the same for every testing framework.

The code looks as follows:

[SetUpFixture]
public class WebApiSetupFixture
{
    private const string WebApiThreadName = "WebApi";

    [OneTimeSetUp]
    public void SetUp() => new Thread(RunWebApi)
    {
        Name = WebApiThreadName
    }.Start();

    private static void RunWebApi()
        => Program.Main(Array.Empty<string>());
    // 'Program' - your main web app class with entry point.
}

Note: The code inside Program.Main(); will also look for connection strings in the MyStaticConfigurationContainer.Configuration which was changed in the previous step.

And that's it! Hope this could help somebody else :)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.