-
Notifications
You must be signed in to change notification settings - Fork 17
1. Getting Started With Grill
amit-shekhar edited this page Sep 27, 2023
·
5 revisions
We follow the standard principle for writing functional tests ->
establish your dependencies, execute the method, verify the output, and perform cleanup.
func TestFunctional(t *testing.T) {
if testing.Short() {
t.Skip("skipping functional test")
}
// setup grills
grills := NewGrills()
if err := grills.StartAll(); err != nil {
t.Fatalf("error starting grills, error=%v", err)
}
defer grills.StopAll()
setupInfra(grills)
setEnv(grills)
runServer(t)
runTests(grills)
}
In this example we need DynamoDB and Kafka grills, the setup for it would look like ->
type Grills struct {
Dynamo *grilldynamo.Dynamo
Kafka *grillkafka.Kafka
}
func NewGrills() *Grills {
return &Grills{
Dynamo: &grilldynamo.Dynamo{},
Kafka: &grillkafka.Kafka{},
}
}
func (grills *Grills) StartAll() error {
return grill.StartAll(context.Background(), grills.Dynamo, grills.Kafka)
}
func (grills *Grills) StopAll() {
_ = grill.StopAll(context.Background(), grills.Dynamo, grills.Kafka)
}
Create all Infra Dependencies required by your service like databases, tables, sqs queues, kafka topics etc.
func setupInfra(grills *Grills) error {
if err := grills.Dynamo.CreateTable(testdata.OrderTableRequest()).Stub(); err != nil {
return err
}
if err := grills.Kafka.CreateTopics("newOrderTopic").Stub(); err != nil {
return err
}
return nil
}
Setup environment variables such that the service points to grills for all its dependencies.
func endpoint(host, port string) string {
return fmt.Sprintf("http://%s:%s", host, port)
}
func setEnv(grills *Grills) {
_ = os.Setenv("LOG_LEVEL", "DEBUG")
_ = os.Setenv("DYNAMODB_TABLENAME", "test-table")
_ = os.Setenv("DYNAMODB_REGION", grills.Dynamo.Region())
_ = os.Setenv("DYNAMODB_ENDPOINT", endpoint(grills.Dynamo.Host(), grills.Dynamo.Port()))
_ = os.Setenv("AWS_ACCESS_KEY_ID", grills.Dynamo.AccessKey())
_ = os.Setenv("AWS_SECRET_ACCESS_KEY", grills.Dynamo.SecretKey())
_ = os.Setenv("KAFKA_BOOTSTRAP_SERVERS", grills.Kafka.BootstrapServers())
}
Start the application in a different go routine and wait for health check to pass
func runServer(t *testing.T) {
go server.Run(config.New())
if err := waitForHealthCheck(cfg.AppPort, time.Second*20); err != nil {
t.Fatalf("health check failed, error=%v", err)
}
}
func waitForHealthCheck(appPort int, maxTime time.Duration) error {
maxTimeTicker := time.Tick(maxTime)
ticker := time.Tick(time.Millisecond * 500)
httpClient := http.Client{Timeout: time.Millisecond * 100}
for {
select {
case <-maxTimeTicker:
return fmt.Errorf("health check failed")
case <-ticker:
res, err := httpClient.Get(fmt.Sprintf("http://localhost:%d/health-check", appPort))
if res != nil && res.Body != nil {
res.Body.Close()
}
if err == nil && res.StatusCode == http.StatusOK {
return nil
}
}
}
}
Run your testscases with grill
func runTests(t *testing.T, grills *Grills) error {
grill.Run(t, createOrderTests(grills))
grill.Run(t, getOrderTests(grills))
}
func createOrderTests(grills *Grills) []grill.TestCase {
return []grill.TestCase{
{
Name: "CreateOrder-Success",
Stubs: []grill.Stub{},
Action: func() interface{} {
response, err := createOrder(testData.Order1)
if err != nil {
return grill.ActionOutput("", 0, err != nil)
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
return grill.ActionOutput(string(responseBody), response.StatusCode, nil)
},
Assertions: []grill.Assertion{
grills.AssertOutput("ok", 200, nil)
grills.Dynamo.AssertItem(testData.Order1.DBItem()),
grills.Kafka.AssertMessageCount("newOrderTopic", testData.Order1, 1)
},
Cleaners: []grill.Cleaner{},
}
}
}
func getOrderTests(grills *Grills) []grill.TestCase {
return []grill.TestCase{
{
Name: "GetOrder-NotFound",
Stubs: []grill.Stub{},
Action: func() interface{} {
response, err := getOrder("order_id_invalid")
if err != nil {
return grill.ActionOutput("", 0, err != nil)
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
return grill.ActionOutput(string(responseBody), response.StatusCode, nil)
},
Assertions: []grill.Assertion{
grill.AssertOutput("", 404, nil)
},
Cleaners: []grill.Cleaner{},
},
{
Name: "GetOrder-FoundSuccess",
Stubs: []grill.Stub{
grills.Dynamo.PutItem(testdata.Order1.DBItem)
},
Action: func() interface{} {
response, err := getOrder(testdata.Order1.ID)
if err != nil {
return grill.ActionOutput("", 0, err != nil)
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
return grill.ActionOutput(string(responseBody), response.StatusCode, nil)
},
Assertions: []grill.Assertion{
grill.AssertOutput(testdata.Order1.String(), 200, nil)
},
Cleaners: []grill.Cleaner{
grills.EmptyDynamoDBTable()
},
}
}
}