Testing
Testing is a first-class citizen in the Tako ecosystem. Because Tako relies heavily on the Service Container and Dependency Injection, your code is naturally decoupled, making it incredibly easy to mock dependencies and write robust tests.
Tako embraces the philosophy that 100% test coverage is not enough. We highly encourage the use of Mutation Testing to ensure your tests actually verify the underlying logic.
Testing the Container
One of the biggest advantages of using Tako's Service Container is how easy it makes writing tests. You don't need complex mocking frameworks to intercept global variables.
Simply create an empty tako.NewApp() instance in your test, bind a mock version of your service, and execute your logic.
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gettako.dev/tako"
"gettako.dev/tako/contracts"
)
// A simple mock for our database
type MockDatabase struct {}
func (m *MockDatabase) GetUser(id int) string {
return "Mocked User"
}
func TestUserService(t *testing.T) {
// 1. Arrange: Create the app and bind the mock
app := tako.NewApp()
app.Instance("database", &MockDatabase{})
// 2. Act: Resolve your service (it will use the mock DB!)
service := NewUserService(app)
result := service.Fetch(1)
// 3. Assert
assert.Equal(t, "Mocked User", result)
}
Testing CLI Commands
Testing console commands in Tako is straightforward because the contracts.CommandContext isolates the console input and output.
While Tako doesn't provide a built-in mock context out of the box, it's very easy to create a fake ConsoleInput to pass arguments and options directly to your command's Handle
method without actually running the application.
func TestGreetCommand(t *testing.T) {
// Arrange
cmd := &commands.GreetCommand{}
mockCtx := CreateMockCommandContext(app, map[string]string{
"name": "Alice",
})
// Act
err := cmd.Handle(mockCtx)
// Assert
require.NoError(t, err)
// Assert that mockCtx.Output() received the expected string...
}
Mutation Testing
Tako's internal core is tested using go-gremlins for Mutation Testing. We highly recommend you use it for your own applications.
What is Mutation Testing?
Standard code coverage only tells you if a line of code was executed during a test. It does not tell you if your test actually verified the output.
Mutation testing tools like go-gremlins work by intentionally breaking your code (e.g., changing == to !=, or swapping true with false) and running your test suite again.
- If your tests fail, the mutation was "killed" (This is good! Your tests are strong).
- If your tests still pass despite the broken code, the mutation "survived" (This is bad! Your tests are weak).
Best Practices
To write mutation-proof tests:
- Test Boundaries: Always write tests that cover edge cases (e.g., empty strings
"",0,nil, boundaries of slices). - Require vs Assert: Use
require.NoError(t, err)for errors so the test stops immediately if something goes wrong. Useassert.Equalfor value checking. - Avoid Test Logic: Do not use complex
if/elseorforloops inside your test execution blocks. Keep it simple: Arrange, Act, Assert.
# Run standard tests
go test ./...
# Unleash the gremlins to verify test quality!
go run github.com/go-gremlins/gremlins/cmd/gremlins@latest unleash ./...
