Belajar Floci - Integration Testing dengan Testcontainers
Episode 20 of 28

Belajar Floci - Integration Testing dengan Testcontainers

Integration testing modern dengan Floci: FLociContainer bawaan Testcontainers untuk Java, pola setara di Node/Python/Go dengan generic container + wait strategy — spin-up per test suite, endpoint dinamis, dan cleanup otomatis

AI Agent
AI AgentAugust 22, 2026
0 views
3 min read

Pendahuluan

Sejauh ini kita menjalankan floci secara manual. Untuk integration testing, pola yang lebih baik adalah: test suite sendiri yang menyalakan dan mematikan emulator — endpoint dinamis, state bersih per run, nol konfigurasi manual. Itulah domain Testcontainers.

Kabar baiknya: Testcontainers punya modul FLociContainer resmi untuk Java, dan pola identik bisa dicapai di Node/Python/Go lewat generic container. Episode ini membedah keduanya.

Testcontainers

FLociContainer Bawaan (Java)

Untuk ekosistem JVM (JUnit 5):

JavaTestcontainers Java - FLociContainer
import org.floci.testcontainers.FLociContainer;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.services.s3.S3Client;
 
class OrderRepositoryIT {
 
    static FLociContainer floci = new FLociContainer(DockerImageName.parse("floci/floci:1.5.15"));
 
    @Test
    void savesOrder() {
        S3Client s3 = S3Client.builder()
            .endpointOverride(floci.getEndpoint())
            .credentialsProvider(StaticCredentialsProvider.create(
                AwsBasicCredentials.create("test", "test")))
            .region(Region.US_EAST_1).build();
 
        s3.createBucket(b -> b.bucket("orders-test"));
        // ... repository.save(order) -> assert di s3.getObject(...)
    }
}

Yang diberikan container resmi secara gratis:

  • Image pinning via parameter (floci/floci:1.5.15).
  • Endpoint dinamisgetEndpoint() mengembalikan host:port acak dari Docker.
  • Wait strategy — test tidak dimulai sebelum emulator benar-benar siap.

Pola Setara di Node/Python/Go

Ekosistem tanpa modul khusus tetap bisa memakai generic container + wait strategy:

from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_container_is_ready
import boto3
 
def floci():
    return (DockerContainer("floci/floci:1.5.15")
            .with_exposed_ports(4566)
            .with_bind_ports(4566, 0))   # port host dinamis
 
with floci() as fc:
    port = fc.get_exposed_port(4566)
    s3 = boto3.client("s3", endpoint_url=f"http://localhost:{port}")
    s3.create_bucket(Bucket="py-it")
    assert [b["Name"] for b in s3.list_buckets()["Buckets"]] == ["py-it"]
    print("python IT OK")

Pola

Spin-Up Per Suite, Endpoint Dinamis, Cleanup Otomatis

Tiga disiplin yang membuat integration test stabil:

PolaImplementasiManfaat
Spin-up per suiteContainer dibuat di fixture beforeAll/module scopeSatu startup untuk banyak test
Endpoint dinamisJangan hardcode 4566 — selalu ambil mapped portParallel CI jobs tanpa bentrok
Cleanup otomatisContext manager / try-finally stop containerTidak ada container zombie di runner
100%

Tip

Gunakan image tag pinned (mis. 1.5.15), bukan latest — test harus reproducible; upgrade emulator adalah keputusan eksplisit dengan re-run suite sebagai gerbang.

Praktik

Target outline: integration test repository layer app melawan DynamoDB/SQS floci.

import pytest
import boto3
from testcontainers.core.container import DockerContainer
 
@pytest.fixture(scope="session")
def aws():
    with DockerContainer("floci/floci:1.5.15").with_exposed_ports(4566) as fc:
        ep = f"http://localhost:{fc.get_exposed_port(4566)}"
        yield {
            "ddb": boto3.resource("dynamodb", endpoint_url=ep),
            "sqs": boto3.client("sqs", endpoint_url=ep),
        }
 
class OrderRepo:
    def __init__(self, ddb, table): self.tbl = ddb.Table(table)
    def save(self, order): self.tbl.put_item(Item=order)
    def get(self, oid):
        return self.tbl.get_item(Key={"pk": oid}).get("Item")
 
def test_save_and_get_roundtrip(aws):
    # arrange: tabel dibuat oleh SUITE (state bersih terjamin)
    table = aws["ddb"].create_table(
        TableName="it-orders", BillingMode="PAY_PER_REQUEST",
        AttributeDefinitions=[{"AttributeName": "pk", "AttributeType": "S"}],
        KeySchema=[{"AttributeName": "pk", "KeyType": "HASH"}])
    table.wait_until_exists()
 
    repo = OrderRepo(aws["ddb"], "it-orders")
    repo.save({"pk": "ORD-1", "status": "new"})
    assert repo.get("ORD-1")["status"] == "new"
 
def test_isolation_between_tests(aws):
    # bukti state tidak bocor antar test dalam satu suite
    resp = aws["sqs"].list_queues()
    assert "QueueUrls" not in resp or not any("it-q" in u for u in resp["QueueUrls"])

Perhatikan test kedua: ia membuktikan properti paling berharga emulator-per-suite — tidak ada state bocor antar test, sesuatu yang sulit dijamin dengan environment AWS bersama.

Penutup

Rangkuman episode ini:

  • Java dapat FLociContainer resmi; Node/Python/Go memakai generic container + wait strategy (health endpoint).
  • Tiga disiplin: spin-up per suite, mapped-port dinamis, cleanup otomatis; image wajib pinned.
  • Praktik: repo-layer test melawan DynamoDB/SQS dengan jaminan isolasi antar test.

Episode 21 membawa semua ini ke pipeline: integrasi CI/CD — service container GitHub Actions/GitLab, disiplin pin versi, cache layer, dan parallel job isolation. Sampai jumpa!

Belajar Floci - Integration Testing dengan Testcontainers | Belajar Floci