How can I improve my TestContainers with EF tests?
Hi, all:
I've been trying to figure out my favorite way to implement test containers with Entity Framework contexts. I was running into an issue where my tests would persist their database changes between each testcase. To remedy this, I created a transactional test scope that rollsback the changes after each testcase.
For this implementation, I have three files:
1)
TestFactory.cs
: Implements an IAsyncLifetime
which my test test classes inherit from, e.x. public class GuildSettingsRepositoryTests(TestFactory factory) : IClassFixture<TestFactory>
. It contains the PostgreSqlContainer
, an IHost
, and offers a CreateTransactionalScopeAsync
method my testcases call.
2) TransactionalTestScope.cs
: Implements an IAsyncLifetime
which rolls back transactions at the end of the scope. Exposes GetRequiredService<T>
which testcases call to retreive the testable service.
3) GuildSettingsRepositoryTests.cs
: Test class with the previously mentioned definition. Here is a sample of a testcase implementation:
Issue
This works, but calling CreateTransactionalScopeAsync
and GetRequiredService
each testcase seems redundant and, frankly, incorrect. How can I simplify my testcase boilerplate code or prevent my testcase context updates from persisting between tests?65 Replies
TestFactory.cs
TransactionalTestScope.cs
Not sure if the entire GuildSettingsRepositoryTests.cs
is needed so I'll cut it for brevitypersonally, i use Respawn and call
ResetAsync()
in every test
valid part of the arrange stage of arrange, act, assert imoUnknown User•2w ago
Message Not Public
Sign In & Join Server To View
I was under the impression I had to create my own
IAsyncLifetime
for setup and teardown of the MSSQL container. I use the transactions to rollback changes made to the database.Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Ideally my fixture/container persists through the tests but my database has one instance per test. I don't understand how to get that to work though.
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
I think I'm not explaining correctly or I have a wrong understanding somewhere. For definitions:
TestFactory
- my WebApplicationFactory
equivalent. I'm under the impression that this gets its own container.
PostgreSqlContainer
- my database container. I'm under the impression this gets its own container. Ideally this persists through tests because of the overhead
ServiceProvider.GetRequiredService<WordBotDbContext>();
- my database context. Ideally this is cleared out every test.
I can try something like
But I'm not sure how to get the PostgreSqlContainer
connection string into the context
At the end of the day I'm just trying to test my app with TestContainers and Entity Framework, so I'm open to any kind of changesUnknown User•2w ago
Message Not Public
Sign In & Join Server To View
I like using Respawn with Testcontainers, it's basically perfect for this exact use case. I have an article about how to set it up here, I've done some of my own (very basic) performance testing and even completely clearing the db after every single test I never had issues https://daninacan.com/resetting-your-test-database-in-c-with-respawn/
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Trying to remember but I think my perf test was just inserting a single record in my test then telling respawn to clear the db, and I'd loop it 100/1k/10k times. I think even on 10k it only took around 60 secs
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Ideally your tests can all be 100% isolated from one another
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
maybe I'm misunderstanding the question
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
right
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
but if respawn clears it out fast enough that you can just 100% wipe clean and start over between each tests isnt that also good enough?
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
I just had the one instance in my perf test though
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
I think I would only move to multiple container instances if the tests were taking too long, or if different parts of the suite had very different and time consuming seeding or something like that
correct
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
I'd start with just wiping the db and starting fresh on every single test
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
state bleed can be very frustrating to diagnose
well your classes are going to execute in parallel
in xunit at least
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Oh yeah, I see now
You are correct
You would probably have to isolate that by marking your own xunit collection
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Ideally, your tables won't span lots of test classes like that... But I get irl not always ideal
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
xunit runs test collections in parallel, and tests within each collection sequentially. By default, each class is its own collection, which is why classes run in parallel. You can override that though and create your own [CollectionDefiniton]s and mark multiple classes to use the same db container
hopefully im making sense lol
it is midnight over here
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
BTW I'm meaning to test this tomorrow when I'm back at my desktop
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Honestly LGTM!
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
64 gb should suffice
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
Yeah, with default xunit behavior, it can't run in parallel due to reset if you have multiple collections running on the same tables. However, you can also correct that behavior by creating your own xunit collections and tagging your test classes
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
to have say like 20 test classes use only 4 db containers
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
:Ok:
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
dream big
I feel like this part of TestContainers needs some work but it's pretty far out in terms of requirements, so I'm not surprised there isn't a 'one and done' solution to it
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
it would be a nice addition, I'm guessing they consider it out of scope for the library
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
oohh I haven't used that
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
and hopefully I won't have to
:YEP:
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
also unless you have something really specific you dont need the with username and password methods
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
or the database one
the builders come with sensible defaults for some of the values
like for the postgresqlbuilder you can see what default values it has here https://github.com/testcontainers/testcontainers-dotnet/blob/main/src/Testcontainers.PostgreSql/PostgreSqlBuilder.cs
GitHub
testcontainers-dotnet/src/Testcontainers.PostgreSql/PostgreSqlBuild...
A library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions. - testcontainers/testcontainers-dotnet
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
not gonna hurt anything to have them, probably just dont need them
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View
no problem, curious if you come across an epiphany :Ok:
Unknown User•2w ago
Message Not Public
Sign In & Join Server To View