Repository navigation
Remove the need for AbstractStartedContainer wrappers in modules #1563
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or requestmajorAn incompatible API changeAn incompatible API change
Description
What happens
Every module is two classes joined by a forwarding wrapper. Simplified from the redis module:
export class RedisContainer extends GenericContainer {
public override async start(): Promise<StartedRedisContainer> {
return new StartedRedisContainer(await super.start(), this.password);
}
}
export class StartedRedisContainer extends AbstractStartedContainer {
constructor(startedTestContainer: StartedTestContainer, private readonly password?: string) {
super(startedTestContainer);
}
}AbstractStartedContainer holds a StartedTestContainer and forwards each of its 20 methods to it.
- 50 module classes extend it.
- 50 module files override
start(), and each override has to wrap the result ofsuper.start()in the module's started class. - Every method added to
StartedTestContainerhas to be written three times: on the interface, onStartedGenericContainerand as a forwarder onAbstractStartedContainer. - Forwarding has caused bugs: AbstractStartedContainer asyncDispose should call this.stop #1202, where
Symbol.asyncDisposecalled the innerstop()and skipped the subclass's. - There are two sets of stop hooks:
containerStopping/containerStoppedonAbstractStartedContainer, andcontainerIsStopping/containerIsStoppedonStartedGenericContainer, which nothing overrides. That class is not exported. AbstractStoppedContainerrepeats the pattern and is used once (selenium).- Overriding
start()to wrap is also where modules put configuration, which is whystart()is not repeatable (Makestart()safe to call more than once on the same container #1520).
Expected
A module author declares the started type once, with no forwarding class and no start() override just to wrap. Some directions:
- Make
GenericContainergeneric over its started type, with a protected factory that the basestart()calls. Configuration moves to a hook such asbeforeContainerCreated. - Export a started base class that module containers extend directly, and let the builder say which class to construct. Inheritance replaces forwarding.
- One class for both states, as Java and Go have.
Where
packages/testcontainers/src/generic-container/abstract-started-container.tspackages/testcontainers/src/generic-container/abstract-stopped-container.tspackages/testcontainers/src/generic-container/started-generic-container.ts- every
packages/modules/*/src/*-container.ts
Notes
- Java and Go use a single type for a configured and a running container. .NET splits builder and container like this repo, but the module container extends the base container directly (
PostgreSqlContainer : DockerContainer, IDatabaseContainer), with no forwarding layer. - Compose and the reuse path construct
StartedGenericContainerthemselves, so the design has to cover those. - Semver: labelled
majorfor removingAbstractStartedContainer. The replacement can ship first in aminor, with the wrapper deprecated.
Dependencies
- Blocked by Make
start()safe to call more than once on the same container #1520, Add lifecycle hooks that do not need a subclass #1554: modules configure themselves insidestart()and through protected hooks. Both need their replacement first. - Coordinate with Call the lifecycle hooks and log consumer when a container is reused #1521: hook semantics for reused containers.
- Coordinate with Converge connection getter and command helper names across modules #1538: both edit every module's started class.
- Land after the started-container additions (Add
getLogs()and a line-based log consumer #1544, Addinspect()and state accessors to started containers #1545, Add endpoint and URL helpers for mapped ports #1546, Unify the copy-to-container methods and add reading a file from a container #1547, Extendexec: reject on failure, stdin, streaming output and a timeout #1548, Addpause(),unpause()andkill()to started containers #1549, Make stopped containers restartable, or drop the empty stopped types #1541). They add forwarders that this removes.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestmajorAn incompatible API changeAn incompatible API change
Activity