This project demonstrates a simple CRUD (Create, Read, Update, Delete) API built using the Gin framework in Golang. The API interacts with a MariaDB database, and the project includes unit tests that utilize MariaDB test containers for isolated and reproducible testing.
- CRUD API: Endpoints for managing
Classroom
andStudent
entities. - MariaDB Integration: Database connection and schema setup.
- Test Containers: Automated testing with MariaDB test containers for a clean and isolated environment.
- Best Practices: Code comments and structure following Golang best practices.
main.go
: Entry point for the API server.handlers.go
: Contains the API handlers for CRUD operations.models.go
: Defines theClassroom
andStudent
models.db.go
: Database connection setup.schema.sql
: SQL schema for creating the database tables.main_test.go
: Unit tests for the API using MariaDB test containers.
- Go (1.20 or later)
- Docker (for test containers)
- MariaDB client (optional, for manual database inspection)
git clone https://github.com/terenzio/MariaDBTestContainerGolang.git
cd MariaDBTestContainerGolang
go mod tidy
Start the API server locally:
go run .
The server will start on http://localhost:8080
.
- GET
/classrooms
: Retrieve all classrooms. - POST
/classrooms
: Create a new classroom. - GET
/classrooms/:id
: Retrieve a specific classroom by ID. - PUT
/classrooms/:id
: Update a specific classroom by ID. - DELETE
/classrooms/:id
: Delete a specific classroom by ID.
- GET
/students
: Retrieve all students. - POST
/students
: Create a new student. - GET
/students/:id
: Retrieve a specific student by ID. - PUT
/students/:id
: Update a specific student by ID. - DELETE
/students/:id
: Delete a specific student by ID.
Run the unit tests, which use MariaDB test containers:
go test ./...
The database schema is defined in schema.sql
. It includes two tables:
classroom
: Stores classroom information.student
: Stores student information, with a foreign key referencingclassroom
.
The tests use the testcontainers-go
library to spin up a MariaDB container. The container is configured with:
MYSQL_ROOT_PASSWORD
: Root password for the database.MYSQL_DATABASE
: Name of the test database.
The schema is loaded into the container during test setup, ensuring a clean environment for each test run.
Testcontainers is a powerful library for managing Docker containers in tests. It provides several benefits, especially for integration testing with databases like MariaDB. Below are some key advantages and examples from this repository:
Each test run starts with a fresh MariaDB container, ensuring that tests do not interfere with each other. This eliminates the risk of flaky tests caused by leftover data or state from previous runs.
Example:
In main_test.go
, the setupTestDB
function initializes a new MariaDB container for each test:
containerReq := testcontainers.ContainerRequest{
Image: "mariadb:latest",
Env: map[string]string{"MYSQL_ROOT_PASSWORD": "root", "MYSQL_DATABASE": "testdb"},
ExposedPorts: []string{"3306/tcp"},
WaitingFor: wait.ForListeningPort("3306/tcp"),
}
mariaC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: containerReq,
Started: true,
})
By using Docker containers, the tests run in a consistent environment regardless of the host system. This ensures that the tests behave the same way on any developer's machine or CI/CD pipeline.
Example: The database schema is loaded into the container during test setup, ensuring a clean and consistent database state:
schema, err := os.ReadFile("schema.sql")
statements := strings.Split(string(schema), ";")
for _, stmt := range statements {
stmt = strings.TrimSpace(stmt)
if stmt != "" {
_, execErr := testDB.Exec(stmt)
if execErr != nil {
t.Fatalf("Could not exec statement: %v", execErr)
}
}
}
Testcontainers abstracts away the complexity of managing Docker containers. With just a few lines of code, you can start a container, configure it, and connect to it.
Example:
The testcontainers-go
library makes it easy to map ports and retrieve the container's host and port:
host, _ := mariaC.Host(ctx)
port, _ := mariaC.MappedPort(ctx, "3306")
dsn := fmt.Sprintf("root:root@tcp(%s:%s)/testdb?parseTime=true", host, port.Port())
testDB, err = sql.Open("mysql", dsn)
Testcontainers ensures that containers are stopped and removed after the tests are completed, preventing resource leaks.
Example:
The setupTestDB
function returns a teardown function to clean up resources:
return func() {
testDB.Close()
mariaC.Terminate(ctx)
}
Testcontainers allows you to test your application with real dependencies (e.g., MariaDB) instead of mocks or in-memory databases. This provides greater confidence in the correctness of your code.
Example:
The tests in main_test.go
interact with a real MariaDB instance, verifying the CRUD operations:
// Test Create Classroom
w := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/classrooms", strings.NewReader(`{"name":"Math"}`))
req.Header.Set("Content-Type", "application/json")
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
By leveraging Testcontainers, this project ensures robust and reliable integration tests, making it easier to catch issues early in the development process.
Feel free to fork this repository and submit pull requests for improvements or additional features.
This project is licensed under the MIT License. See the LICENSE
file for details.
Happy coding!