Testcontainers for Go with Dolt
Recently, a customer reached out to our team asking for a blog using Dolt with Testcontainers, "an open source framework for providing throwaway, lightweight instances of databases, message brokers, web browsers, or just about anything that can run in a Docker container."
I'd not encountered Testcontainers prior to this request, but was excited to investigate using Dolt with this framework, as well as showing off some of the unique and powerful features of Dolt that make it a compelling testing, and production, database.
For those who don't know, if MySQL and Git had a baby, it would be Dolt—a version controlled SQL database with cloning, branching, and merging that can be used as a drop in replacement for a MySQL instance.
Those familiar with running tests against traditional databases know that it's common practice to carefully use SQL transactions to manage the state of the database since tests mutate the database with edits. Most often transactions will be rolled back after a single test finishes so that a subsequent test begins with the desired database state.
What's cool about Dolt is that you can avoid having to manage the SQL transactions in your test framework altogether by simply executing each test on its own database branch.
Like a branch in Git, branches in Dolt contain isolated, independent working sets where changes exist only on the branch itself until they are committed and merged into other branches. One simply needs to create a new branch from the Dolt commit that contains the desired starting database state.
Additionally, because Dolt databases can be cloned like Git repositories can be cloned, setting up the initial state of a database is easy as well.
Traditional databases are typically seeded with mock data from a SQL file or some generated data from seeding code. Often this data does not reflect production data since actual production data can be so difficult to get into the testing environment.
With Dolt, though, getting production data into a testing environment is trivial. You simply need a copy of the production data hosted on a remote, like DoltHub, and then the test database can clone it locally, making it ready to test against.
To demonstrate this I created a repository showcasing these cool features of Dolt and how Testcontainers makes testing incredibly easy.
Getting Started with Testcontainers for Go and Dolt
When I discovered Testcontainers I followed their post Getting Started with Testcontainers for Go which creates an example repository that runs tests against PostgreSQL.
In the example repo, they use the built-in PostgreSQL module API to create and destroy the PostgreSQL Testcontainer. They also provide a GenericContainer
implementation which is useful for setting up containers that aren't officially supported, which one could do for Dolt, but, we thought it would be better for Dolt to be an officially supported Testcontainers module 🤠.
So, I opened a pull request that's currently in review, that adds official support for Dolt, at least in Golang.
Next, I, created a similar sample repo from the Testcontainers article, but used the new Dolt module API from my pull request in place of PostgreSQL. I also changed the testing harness to leverage Dolt's unique branching and cloning features. Let's dive in!
Create Customer and Repository
Like the original article instructs, we start out with a Customer
and Repository
definition in their respective files, customer/types.go
and customer/repo.go
.
// customer/types.go
package customer
type Customer struct {
Id int
Name string
Email string
}
// customer/repo.go
package customer
import (
"context"
"database/sql"
"errors"
// Import mysql into the scope of this package (required)
_ "github.com/go-sql-driver/mysql"
)
type Repository struct {
db *sql.DB
}
func NewRepository(ctx context.Context, db *sql.DB) (*Repository, error) {
return &Repository{
db: db,
}, nil
}
func (r Repository) CreateCustomer(ctx context.Context, customer *Customer) (*Customer, error) {
res, err := r.db.ExecContext(ctx, "INSERT INTO customers (name, email) VALUES (?, ?);", customer.Name, customer.Email)
if err != nil {
return nil, err
}
id, err := res.LastInsertId()
if err != nil {
return nil, err
}
customer.Id = int(id)
return customer, nil
}
func (r Repository) GetCustomerByEmail(ctx context.Context, email string) (*Customer, error) {
customer := Customer{}
row := r.db.QueryRowContext(ctx, "SELECT id, name, email FROM customers WHERE email = ?;", email)
err := row.Scan(&customer.Id, &customer.Name, &customer.Email)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &customer, nil
}
func (r Repository) UpdateCustomer(ctx context.Context, customer *Customer) error {
_, err := r.db.ExecContext(ctx, "UPDATE customers SET name = ?, email = ? WHERE id = ?;", customer.Name, customer.Email, customer.Id)
return err
}
func (r Repository) DeleteCustomer(ctx context.Context, customer *Customer) error {
_, err := r.db.ExecContext(ctx, "DELETE from customers WHERE id = ?;", customer.Id)
return err
}
Customer
is straightforward, simplified customer data and Repository
is the interface for accessing the underlying database, Dolt, instead of PostgreSQL.
One change I made from the original sample repo was to change the NewRepository
arguments to accept a *sql.DB
instead of a connection string. I did this so that the Repository
would not need to handle any configuration of the database connection, and would simple operate against any *sql.DB
it received.
This change was necessary to have each test executing on a different branch, but we'll cover that in more detail shortly.
I also added UpdateCustomer
and DeleteCustomer
Repository
methods to better illustrate that tests can modify the database freely and run in any order, without affecting any other tests. A test could even drop the customers
table and that change would only occur on the distinct branch for that specific test 🤯!
Add Seeding Scripts
The next step the article has you do is add the script testdata/init-db.sql
that runs during database initialization and seeds the database with some data. In the PostgreSQL sample repo, it creates a customers
table and inserts a single customer "John".
We add a similar script in our sample repo as well, called testdata/init-db.sql
that contains:
CREATE DATABASE IF NOT EXISTS test_db;
USE test_db;
CREATE TABLE IF NOT EXISTS customers (id serial, name varchar(255), email varchar(255));
CALL DOLT_COMMIT('-Am', 'create table customers');
INSERT INTO customers(name, email) VALUES ('John', 'john@gmail.com');
CALL DOLT_COMMIT('-Am', 'insert john into customers');
First, notice we must create the database in our initialization script, whereas the script for PostgreSQL did not. This is because the dolthub/dolt-sql-server Docker image does not create a database for us by default, so we do so in the initialization script.
Next, we create the same customers
table and insert "John" into the table, but after each step we also create Dolt commits using CALL DOLT_COMMIT. This ensures that our main
branch of the database, which will be the parent of each new testing branch, will have the same data when each test runs.
But what if we don't want to create a new database, and instead want to clone an existing one to test?
We can do so by adding a different initialization script called testdata/clone-db.sh
which contains:
#!/bin/bash
# use credentials for remote
if [ -n "$DOLT_CREDS_PUB_KEY" ]; then
dolt creds use "$DOLT_CREDS_PUB_KEY"
fi
# clone
dolt sql -q "CALL DOLT_CLONE('$DOLT_REMOTE_CLONE_URL', '$DOLT_DATABASE');"
This file allows us to do exactly that by initializing our Dolt container using CALL DOLT_CLONE, which clones the database to the server.
If the remote database is private
, we can also supply a Dolt credentials file and public key to the Dolt Testcontainer to authorize the Dolt server to clone it.
Once we have these two initialization scripts, it's time to create our Dolt Testcontainers that use them.
Define Dolt Testcontainers
Skipping to the Reusing the containers and running multiple tests as a suite section of the article, it tells you to refactor some previous code and create a new file called testhelpers/containers.go
which contains the instantiation of the PostgreSQL Testcontainer.
We'll add this same file in our repo, but this time, it will contain the Testcontainer setup for our two Dolt containers:
// testhelpers/containers.go
package testhelpers
import (
"context"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/dolt"
"path/filepath"
)
type DoltContainer struct {
*dolt.DoltContainer
ConnectionString string
}
func CreateDoltContainer(ctx context.Context) (*DoltContainer, error) {
doltContainer, err := dolt.RunContainer(ctx,
testcontainers.WithImage("dolthub/dolt-sql-server:latest"),
dolt.WithScripts(filepath.Join("..", "testdata", "init-db.sql")),
dolt.WithDatabase("test_db"),
dolt.WithUsername("tester"),
dolt.WithPassword("testing"),
)
if err != nil {
return nil, err
}
connStr, err := doltContainer.ConnectionString(ctx)
if err != nil {
return nil, err
}
return &DoltContainer{
DoltContainer: doltContainer,
ConnectionString: connStr,
}, nil
}
func CreateDoltContainerFromClone(ctx context.Context) (*DoltContainer, error) {
doltContainer, err := dolt.RunContainer(ctx,
testcontainers.WithImage("dolthub/dolt-sql-server:latest"),
dolt.WithScripts(filepath.Join("..", "testdata", "clone-db.sh")),
dolt.WithDatabase("test_db"),
dolt.WithUsername("tester"),
dolt.WithPassword("testing"),
dolt.WithDoltCloneRemoteUrl("https://doltremoteapi.dolthub.com/dolthub/testcontainers-go-demo"),
)
if err != nil {
return nil, err
}
connStr, err := doltContainer.ConnectionString(ctx)
if err != nil {
return nil, err
}
return &DoltContainer{
DoltContainer: doltContainer,
ConnectionString: connStr,
}, nil
}
Above you'll see that CreateDoltContainer
creates a DoltContainer
initialized with the testdata/init-db.sql
script, so will only have "John" in the customers
table.
And you'll also see CreateDoltContainerFromClone
which uses the testdata/clone-db.sh
to clone this DoltHub database during initialization. This remote database does not contain "John", but instead contains "Vicki", "Megan", and "Lisa".
We'll use these different DoltContainer
s to run two test suites, one against the locally seeded Dolt database and one against the remotely seeded Dolt database.
Create Test Suites
Looking back at the original article, we next need to create a file called customer/repo_suite_test.go
that contains the testify suite implementation and some tests defined on the CustomerRepoTestSuite
.
For our locally seeded database will define a similar test suite, but for readability we'll define the tests in a separate file called customer/repo_test.go
.
// customer/repo_test_suite.go
package customer
import (
"context"
"database/sql"
"fmt"
"log"
"testing"
"github.com/dolthub/testcontainers-go-demo/testhelpers"
"github.com/stretchr/testify/suite"
)
type CustomerRepoTestSuite struct {
suite.Suite
doltContainer *testhelpers.DoltContainer
repository *Repository
db *sql.DB
ctx context.Context
startRef string
}
func (suite *CustomerRepoTestSuite) SetupSuite() {
suite.ctx = context.Background()
doltContainer, err := testhelpers.CreateDoltContainer(suite.ctx)
if err != nil {
log.Fatal(err)
}
suite.doltContainer = doltContainer
if suite.startRef == "" {
suite.startRef = "main"
}
}
func (suite *CustomerRepoTestSuite) TearDownSuite() {
if err := suite.doltContainer.Terminate(suite.ctx); err != nil {
log.Fatalf("error terminating dolt container: %s", err)
}
}
func (suite *CustomerRepoTestSuite) createBranchName(suiteName, testName string) string {
return fmt.Sprintf("%s_%s", suiteName, testName)
}
func (suite *CustomerRepoTestSuite) AfterTest(suiteName, testName string) {
if suite.db != nil {
branchName := suite.createBranchName(suiteName, testName)
_, err := suite.db.ExecContext(suite.ctx, "CALL DOLT_COMMIT('-Am', ?)", fmt.Sprintf("Finished testing on %s", branchName))
if err != nil {
if !strings.Contains(err.Error(), "nothing to commit") {
log.Fatal(err)
}
}
err = suite.db.Close()
if err != nil {
log.Fatal(err)
}
suite.db = nil
suite.repository = nil
}
}
func (suite *CustomerRepoTestSuite) BeforeTest(suiteName, testName string) {
// connect to the dolt server
db, err := sql.Open("mysql", suite.doltContainer.ConnectionString)
if err != nil {
log.Fatal(err)
}
newBranch := suite.createBranchName(suiteName, testName)
// checkout new branch for test from the designated starting point
_, err = db.ExecContext(suite.ctx, "CALL DOLT_CHECKOUT(?, '-b', ?);", suite.startRef, newBranch)
if err != nil {
log.Fatal(err)
}
suite.db = db
repository, err := NewRepository(suite.ctx, suite.db)
if err != nil {
log.Fatal(err)
}
suite.repository = repository
}
func TestCustomerRepoTestSuite(t *testing.T) {
suite.Run(t, new(CustomerRepoTestSuite))
}
In our version of CustomerRepoTestSuite
, the SetupSuite
method, called at the start of a test suite run, uses testhelpers.CreateDoltContainer
to create a Dolt Testcontainer from testdata/init-db.sql
. TearDownSuite
, run after the test suite completes, terminates the Dolt Testcontainer, cleaning up any resources it used.
BeforeTest
is the method we use on our suite to ensure that each test is run on a distinct Dolt branch. As its name suggests, this method, called before each test, opens a connection to the Dolt database, executes CALL DOLT_CHECKOUT to create and checkout a new branch for the given test, then instantiates a new Repository
that will operate against that database connection on that new branch.
AfterTest
runs after each test and attempts to commit any changes that occurred during testing. It then closes the open database connection before resetting the suite's db
and repository
fields. It's certainly possible to add additional logic to delete the testing branches after each test (and they will be deleted by Testcontainers anyway), but we can use these lingering branches later to do some debugging if need be. More on that later.
TestCustomerRepoTestSuite
is what's called to run this test suite and all of its tests.
Here are the tests I've defined for this suite:
// customer/repo_test.go
package customer
import "github.com/stretchr/testify/assert"
func (suite *CustomerRepoTestSuite) TestCreateCustomer() {
t := suite.T()
customer, err := suite.repository.CreateCustomer(suite.ctx, &Customer{
Name: "Henry",
Email: "henry@gmail.com",
})
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.NotNil(t, customer.Id)
}
func (suite *CustomerRepoTestSuite) TestGetCustomerByEmail() {
t := suite.T()
customer, err := suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "John", customer.Name)
assert.Equal(t, "john@gmail.com", customer.Email)
}
func (suite *CustomerRepoTestSuite) TestUpdateCustomer() {
t := suite.T()
customer, err := suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "John", customer.Name)
assert.Equal(t, "john@gmail.com", customer.Email)
id := customer.Id
customer.Name = "JohnAlt"
customer.Email = "john-alt@gmail.com"
err = suite.repository.UpdateCustomer(suite.ctx, customer)
assert.NoError(t, err)
customer, err = suite.repository.GetCustomerByEmail(suite.ctx, "john-alt@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "JohnAlt", customer.Name)
assert.Equal(t, "john-alt@gmail.com", customer.Email)
assert.Equal(t, id, customer.Id)
}
func (suite *CustomerRepoTestSuite) TestDeleteCustomer() {
t := suite.T()
customer, err := suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "John", customer.Name)
assert.Equal(t, "john@gmail.com", customer.Email)
err = suite.repository.DeleteCustomer(suite.ctx, customer)
assert.NoError(t, err)
customer, err = suite.repository.GetCustomerByEmail(suite.ctx, "john@gmail.com")
assert.NoError(t, err)
assert.Nil(t, customer)
}
They contain the original tests from the Testcontainers article but with UpdateCustomer
and DeleteCustomer
tests added.
Now let's create a test suite that uses the remote data from the database hosted on DoltHub.
In another file called customer/clone_suite_test.go
define another test suite like so:
package customer
import (
"context"
"log"
"testing"
"github.com/dolthub/testcontainers-go-demo/testhelpers"
"github.com/stretchr/testify/suite"
)
type RemoteRepoTestSuite struct {
c *CustomerRepoTestSuite
}
func (suite *RemoteRepoTestSuite) T() *testing.T {
return suite.c.T()
}
func (suite *RemoteRepoTestSuite) SetT(t *testing.T) {
suite.c.SetT(t)
}
func (suite *RemoteRepoTestSuite) SetS(s suite.TestingSuite) {
suite.c.SetS(s)
}
func (suite *RemoteRepoTestSuite) Ctx() context.Context {
return suite.c.ctx
}
func (suite *RemoteRepoTestSuite) Repository() *Repository {
return suite.c.repository
}
func (suite *RemoteRepoTestSuite) SetupSuite() {
suite.c.ctx = context.Background()
doltContainer, err := testhelpers.CreateDoltContainerFromClone(suite.c.ctx)
if err != nil {
log.Fatal(err)
}
suite.c.doltContainer = doltContainer
if suite.c.startRef == "" {
suite.c.startRef = "main"
}
}
func (suite *RemoteRepoTestSuite) TearDownSuite() {
suite.c.TearDownSuite()
}
func (suite *RemoteRepoTestSuite) AfterTest(suiteName, testName string) {
suite.c.AfterTest(suiteName, testName)
}
func (suite *RemoteRepoTestSuite) BeforeTest(suiteName, testName string) {
suite.c.BeforeTest(suiteName, testName)
}
func TestRemoteRepoTestSuite(t *testing.T) {
s := &RemoteRepoTestSuite{c: new(CustomerRepoTestSuite)}
suite.Run(t, s)
}
This test suite can simply wrap CustomerRepoTestSuite
and in its SetupSuite
method it can call testhelpers.CreateDoltContainerFromClone
. The rest of the methods can simply call their underlying equivalent. That's basically it 😅.
Now we just need some tests that execute against this database. Add a file called customer/clone_test.go
that contains:
// customer/clone.go
package customer
import "github.com/stretchr/testify/assert"
func (suite *RemoteRepoTestSuite) TestCreateCustomer() {
t := suite.T()
customer, err := suite.Repository().CreateCustomer(suite.Ctx(), &Customer{
Name: "Henry",
Email: "henry@gmail.com",
})
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.NotNil(t, customer.Id)
}
func (suite *RemoteRepoTestSuite) TestGetCustomerByEmail() {
t := suite.T()
customer, err := suite.Repository().GetCustomerByEmail(suite.Ctx(), "lisa@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "Lisa", customer.Name)
assert.Equal(t, "lisa@gmail.com", customer.Email)
}
func (suite *RemoteRepoTestSuite) TestUpdateCustomer() {
t := suite.T()
customer, err := suite.Repository().GetCustomerByEmail(suite.Ctx(), "vicki@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "Vicki", customer.Name)
assert.Equal(t, "vicki@gmail.com", customer.Email)
id := customer.Id
customer.Name = "Samantha"
customer.Email = "samantha@gmail.com"
err = suite.Repository().UpdateCustomer(suite.Ctx(), customer)
assert.NoError(t, err)
customer, err = suite.Repository().GetCustomerByEmail(suite.Ctx(), "samantha@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "Samantha", customer.Name)
assert.Equal(t, "samantha@gmail.com", customer.Email)
assert.Equal(t, id, customer.Id)
}
func (suite *RemoteRepoTestSuite) TestDeleteCustomer() {
t := suite.T()
customer, err := suite.Repository().GetCustomerByEmail(suite.Ctx(), "megan@gmail.com")
assert.NoError(t, err)
assert.NotNil(t, customer)
assert.Equal(t, "Megan", customer.Name)
assert.Equal(t, "megan@gmail.com", customer.Email)
err = suite.Repository().DeleteCustomer(suite.Ctx(), customer)
assert.NoError(t, err)
customer, err = suite.Repository().GetCustomerByEmail(suite.Ctx(), "megan@gmail.com")
assert.NoError(t, err)
assert.Nil(t, customer)
}
These tests use the same methods as the other test suite, but can expect the data from the remote database instead of the data from the local database. Plus, you get the same isolated one branch per test guarantee without any extra coding.
Now let's say you need to do some debugging by investigating the state of the database for a particular failing test. To do this you can, first, keep the Dolt container alive by adding a time.Sleep()
to the TearDownSuite()
method so that the container is not actually torn down. Then, you'll need to connect to the Dolt container with a mysql
client on the port used by Docker.
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54101e344f2f dolthub/dolt-sql-server:latest "tini -- docker-entr…" 8 seconds ago Up 8 seconds 0.0.0.0:64579->3306/tcp, 0.0.0.0:64580->33060/tcp charming_colden
a407c7fb6826 testcontainers/ryuk:0.6.0 "/bin/ryuk" 9 seconds ago Up 8 seconds 0.0.0.0:64574->8080/tcp reaper_b35ad4ba6ffb051b3e86910b96c2e5107ac275eddea49d6b43f2d7ca7955a272
Running docker ps
will show that port 64579
is being forwarded to 3306
on the dolthub/dolt-sql-server
container, so we can connect with:
mysql --host 0.0.0.0 --port 64579 -uroot
From here, we can select our database called test_db
and show the branches in that database that our tests made:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| test_db |
+--------------------+
3 rows in set (0.02 sec)
mysql> use test_db;
mysql> select * from dolt_branches;
+----------------------------------------------+----------------------------------+------------------+------------------------+---------------------+--------------------------------------------------------------+--------+--------+
| name | hash | latest_committer | latest_committer_email | latest_commit_date | latest_commit_message | remote | branch |
+----------------------------------------------+----------------------------------+------------------+------------------------+---------------------+--------------------------------------------------------------+--------+--------+
| CustomerRepoTestSuite_TestCreateCustomer | 4hdjr4n4pj2ocuab0pkjd22sgh1do42v | tester | tester@% | 2024-02-01 00:30:30 | Finished testing on CustomerRepoTestSuite_TestCreateCustomer | | |
| CustomerRepoTestSuite_TestDeleteCustomer | fm1vi42gs5a8v1m8j9eekd7p6fe9fl01 | tester | tester@% | 2024-02-01 00:30:30 | Finished testing on CustomerRepoTestSuite_TestDeleteCustomer | | |
| CustomerRepoTestSuite_TestGetCustomerByEmail | 35ppio7l1uod8irfbkp1uh4kcakrc99m | root | root@localhost | 2024-02-01 00:30:29 | insert john into customers | | |
| CustomerRepoTestSuite_TestUpdateCustomer | vokoftbvpe4altvbc08g052l26228350 | tester | tester@% | 2024-02-01 00:30:30 | Finished testing on CustomerRepoTestSuite_TestUpdateCustomer | | |
| main | 35ppio7l1uod8irfbkp1uh4kcakrc99m | root | root@localhost | 2024-02-01 00:30:29 | insert john into customers | | |
+----------------------------------------------+----------------------------------+------------------+------------------------+---------------------+--------------------------------------------------------------+--------+--------+
5 rows in set (0.02 sec)
Finally, we can view the diff created by the test simply by querying the dolt_commit_diff_$tablename
table. Here's the diff created by the test TestDeleteCustomer
:
mysql> select * from dolt_commit_diff_customers where to_commit = HASHOF('CustomerRepoTestSuite_TestDeleteCustomer') and from_commit = HASHOF('main');
+-------+---------+----------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
| to_id | to_name | to_email | to_commit | to_commit_date | from_id | from_name | from_email | from_commit | from_commit_date | diff_type |
+-------+---------+----------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
| NULL | NULL | NULL | fm1vi42gs5a8v1m8j9eekd7p6fe9fl01 | 2024-02-01 00:30:30.244 | 1 | John | john@gmail.com | 35ppio7l1uod8irfbkp1uh4kcakrc99m | 2024-02-01 00:30:29.225 | removed |
+-------+---------+----------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
1 row in set (0.04 sec)
"John" was removed by this test, as expected. And here is the diff for TestUpdateCustomer
:
mysql> select * from dolt_commit_diff_customers where to_commit = HASHOF('CustomerRepoTestSuite_TestUpdateCustomer') and from_commit = HASHOF('main');
+-------+---------+--------------------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
| to_id | to_name | to_email | to_commit | to_commit_date | from_id | from_name | from_email | from_commit | from_commit_date | diff_type |
+-------+---------+--------------------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
| NULL | NULL | NULL | vokoftbvpe4altvbc08g052l26228350 | 2024-02-01 00:30:30.307 | 1 | John | john@gmail.com | 35ppio7l1uod8irfbkp1uh4kcakrc99m | 2024-02-01 00:30:29.225 | removed |
| 1 | JohnAlt | john-alt@gmail.com | vokoftbvpe4altvbc08g052l26228350 | 2024-02-01 00:30:30.307 | NULL | NULL | NULL | 35ppio7l1uod8irfbkp1uh4kcakrc99m | 2024-02-01 00:30:29.225 | added |
+-------+---------+--------------------+----------------------------------+-------------------------+---------+-----------+----------------+----------------------------------+-------------------------+-----------+
2 rows in set (0.03 sec)
We can see that the update resulted in a removal of "John" and the addition of "JohnAlt", which is what we expected here as well.
You're now testing with Dolt and TestContainers in Go!
If you'd like to try testing your production MySQL database with Dolt and Testcontainers, you'll need to create a SQL dump of your database and add it to the Dolt container initialization scripts with dolt.WithScripts()
.
Alternatively, you can import the dump into a Dolt database hosted on either DoltHub or DoltLab, and follow the steps for cloning into a Dolt container described above.
Happy testing!
Conclusion
Hopefully you find this guide helpful, we'd love for you to give Dolt and Testcontainers a try. We're always on the hunt for different tools and integrations we can pair with Dolt that add value to our customers.
Feel free to come by our Discord and let us know how you're using Dolt and TestContainers, or if there are other products or tools you think make sense with Dolt.
Don't forget to check out each of our different product offerings below, to find which ones are right for you:
- Dolt—it's Git for data.
- DoltHub—it's GitHub for Dolt.
- DoltLab—it's GitLab for data.
- Hosted Dolt—it's RDS for Dolt databases.