Building APIs with C# and ASP.NET Core

Testcontainers & Real Databases

Spencer Schneidenbach

Spencer Schneidenbach

Aviron Software, Microsoft MVP
Building APIs with C# and ASP.NET Core

Check out a free preview of the full Building APIs with C# and ASP.NET Core course

The "Testcontainers & Real Databases" Lesson is part of the full, Building APIs with C# and ASP.NET Core course featured in this preview video. Here's what you'd learn in this lesson:

Spencer introduces Testcontainers, a library that allows you to spin up real databases in containers for your tests. A PostgreSQL database is added to the project and run using Docker.

Preview
Close

Transcript from the "Testcontainers & Real Databases" Lesson

[00:00:00]
>> Spencer Schneidenbach: Test containers and real databases. So you may recall that I mentioned that the F core in memory database is not recommended to be used by anybody, it doesn't be recommended. Nobody at Microsoft would recommend it. No developer who knows better would recommend it. It's not useful and it shouldn't be used at all.

[00:00:16]
But if you had me in a corner, I would tell you that I would prefer you write tests with that than not write tests at all. So I think we've demonstrated that the SQLite in memory database is a perfect substitute because it actually is a real database in many ways.

[00:00:31]
But you're not using SQLite in production. You're probably not using it in production at all. You might be using it for a small app that might run on a remote server somewhere that needs to track its own data. But for a SaaS product or an ASP.NET Core web app, you're not gonna be using SQLite.

[00:00:47]
You're probably gonna use something like SQL Server or MySQL or Postgres. So wouldn't it be easy if you could have a test that spins up a real production like database and tears it down cleanly? Well, I wanted to introduce you to something called testcontainer, this is something I use a lot, which is a library that allows you to spin up real databases in containers for your test.

[00:01:07]
So I do want to just walk through this code. I'm not gonna walk through a refactor of it. This is where we are going to go to file, and we're gonna go to open folder, and we're gonna 4z final, and we're gonna look at the kind of the final product of this entire course.

[00:01:24]
I'm gonna go to solutions explore, and I just wanna walk you through a couple of things. I wanna show you that in our program.cs, we are actually creating a PostgreSql container instance. So this is using a NuGet package Testcontainers.PostgreSQL. Should also mention at this point that because we're using containers, this does require Docker, and I have it running up here.

[00:01:46]
You can see that my Docker desktop is running. If I open my dashboard, I don't see any containers. You don't have to have a ton of experience with Docker to use this library. But Docker is definitely something that I recommend that you look into, especially considering you will probably Probably host some of your ASP NET core apps on Docker.

[00:02:03]
At some point you're going to see that I've removed all references to SQLite. Instead I'm using NPG, which stands for n.net Postgres SQL. I've got my query tracking behavior turned off, which is fine. I'm going to go to here and show you the project file to see that SQLite is truly and entirely gone.

[00:02:25]
What I basically done is basically when you start up this app, it will actually start up. And just with these two lines of code, will actually create and run a Postgres database for you. And we can see that because down here you can see that, if we caught it real quick.

[00:02:44]
>> Spencer Schneidenbach: We actually are starting a container. And if we switch back over to our Docker instance, we can see that we've got some containers running. And it's actually done the migrations. It's seeded the database and it's actually running against a real Postgres database. So if you're using Postgres or using SQL Server, whichever database you use, it doesn't really matter, something other than SQLite for most production apps.

[00:03:09]
This is a really useful thing and it's really useful because without it, you'd have to do a couple of weird things to deal with some kind of idiosyncrasies between different database providers. Take for instance, these migrations and migrations are probably the biggest reason to just use test containers as opposed to a SQLite in memory database.

[00:03:30]
So we can create this table here. You see, you notice that this call to create table here actually looks a little bit different. The fields are a little bit different if you go back and compare them. The data types are different. That's because data types are not ubiquitous across database systems, right?

[00:03:48]
You have SQL server that has varchar and varchar. You have Postgres that simply has text. They are all different, and you can't apply a set of SQLite migrations to Postgres or vice versa. So what I've done is simply just removed a SQLite entirely and just added in the Postgres migration.

[00:04:11]
So I took out all those migrations. There's a couple of techniques you can use to do them apart, to tear them apart, but I don't recommend that you do that. Simply because it's just when you have something as easy to use as test containers, it's usually just not worth the effort.

[00:04:26]
You'll also note that our custom web application factory is a heck of a lot simpler, that's really because I'm spinning up the Postgres SQL container in our program.cs file. In practice, you would probably connect to a real Postgres database there. And then inside of your tests, you would actually use test containers to actually run your tests.

[00:04:45]
So I am kinda cheating a little bit and putting my test container inside of my program.cs, but inevitably it's not going to live there. It's really only going to live inside of your test project. That's where you're gonna have test containers installed. So if I run all these tests, you'll see .NET test.

[00:05:09]
>> Spencer Schneidenbach: It takes a little bit because it takes time to spin up that docker container, a few second penalty, not a big deal. Then you see that all of our tests pass. We did have to make a couple of tweaks to the tests in order to get them to pass.

[00:05:22]
I did find that there was a couple of subtle bugs, and I documented there, because I was using a different database provider, there was a couple of subtle different things between the tests. I actually found a bug in my original code, I went back and checked it all, where I was only returning one record per page, as opposed to 100.

[00:05:40]
So I went back and fixed that. Changing to Postgres actually helped me discover that. So just a little bit more of kind of the behind the scenes, a little bit of the journey. The point of this that I would say, use test containers. It's an awesome abstraction, great for running tests.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now