dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.65k stars 413 forks source link

Support Testcontainers for resource definition #3105

Open eddumelendez opened 5 months ago

eddumelendez commented 5 months ago

Hi, I landed in the project by looking at this issue to support Testcontainers for component tests.

I currently see this is how resources are provisioned:

For Redis container:

builder.AddRedis("basketcache")
    .WithDataVolume()
    .WithRedisCommander();

For Azure Redis:

builder.AddRedis("cache").AsAzureRedis()

Proposal

Provide an API to provide specific Resource definitions, let's say AspireResource. For the Redis example, it can use AzureRedisExtensions or Testcontainers Redis module. The API can be also used in case there are other ways to provision a Redis instance, so, developers can write their own implementation.

For Testcontainers Redis:

var redisContainer = new RedisBuilder()
  .WithImage("redis:7.0")
  .Build();

builder.AddRedis("cache", new TestcontainersAspireResource().WithContainer(redisContainer).Build()))

where TestcontainersAspireResource will start the container.

For Azure Redis:

builder.AddRedis("cache", new AzureAspireResource().WithExtension(new AzureRedisExtension()).Build())

Benefits

Also, by looking at the issues, some of them are already handled by Testcontainers. For example:

All Testcontainers .NET are listed in the module catalog.

If there is no specific module the Testcontainers API is very fluent and allow the user to write their own definition.

var container = new ContainerBuilder()
  .WithImage("testcontainers/helloworld:1.1.0")
  .WithPortBinding(8080, true)
  .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(r => r.ForPort(8080)))
  .Build();

await container.StartAsync()
  .ConfigureAwait(false);

References

In the Java world, this integration has been done in the three major frameworks (Quarkus devservices, Micronaut test-resources, Spring Boot service connection). I would be very happy to chat and share more about it in order to find a similar experience in .NET ecosystem with Aspire.

davidfowl commented 5 months ago

I like the idea of bridging test containers as a custom resource type but I don't think we should aim to unify the API. Something like:

builder.AddTestContainerResource("name", () =>
{
    return new RedisBuilder().WithImage("redis:7.0").Build();
});

Would need to give some more thought to how connection strings etc work in this mode.

Also, by looking at the issues, some of them are already handled by Testcontainers. For example:

That's great but I'm not sure we need to lean on test containers here, the resource model is pretty capable 😄. I'm looking forward to an experiment in this space to see what comes out!

Thanks for the issue!

ElanHasson commented 4 months ago

fyi, I'm using test containers here not as a test, but for dev only : https://github.com/InfinityFlowApp/aspire-temporal/blob/main/src/InfinityFlow.Aspire.Temporal/TemporalServerContainerBuilderExtensions.cs#L31-L70

(yes, i later learned you can use temporal-admin image which has temporal-cli and many other things)

I do agree having a resource that can "speak test container" natively would be awesome.

davidfowl commented 4 months ago

Unrelated: @ElanHasson if you want to make your resource plug in a little more cleanly, you can write an IDistributedApplicationLifecycleHook that uses test containers to build container image.

https://github.com/InfinityFlowApp/aspire-temporal/blob/19ed3f9cc63b65951206d9cffccb0232dc89b371/src/InfinityFlow.Aspire.Temporal/TemporalServerContainerBuilderExtensions.cs#L101

This might be a very nice integration point between aspire and test containers.

MadL1me commented 3 months ago

I'm in big support for this issue, because it solves the problem of any custom integration (I was thinking about support for https://zitadel.com/ as IDP)