A bite for API test with ‘Golang Testify’ and ‘Python Pytest’

Based on my experience in automation testing across various companies, I’ve utilized ‘Golang Testify’ and ‘Python Pytest’. Transitioning from a manual testing background to embracing automation testing, I’ve gained valuable insights. I would like to share some insights about these two frameworks and document my thoughts for future reference.

Testing Goal

We will use REST API as the testing target for our article, specifically utilizing the Public API provided by Platzi Fake Store API as our test data source.

Trend

Both are well-known in the field of automation testing. Their GitHub stars, each have garnered an impressive following of over 10k.

Mentioned Topics Will Be Covered

▶ Get Started Testing
· Test Naming Conventions
· Assertion
· Running Tests With Parallel
▶ After Testing
· Generate Test Report
▶ My Insight
▶ Conclusion

Get Started Testing

Before testing, we shall be understanding the response result, and then do the testing.

[GET] https://api.escuelajs.co/api/v1/products/50
{
"id": 50,
"title": "Practical Plastic Bike",
"price": 911,
"description": "New range of formal shirts are designed keeping you in mind. With fits and styling that will make you stand apart",
"images": [
"https://i.imgur.com/0qQBkxX.jpg",
"https://i.imgur.com/I5g1DoE.jpg",
"https://i.imgur.com/myfFQBW.jpg"
],
"creationAt": "2023-12-24T10:24:11.000Z",
"updatedAt": "2023-12-24T10:24:11.000Z",
"category": {
"id": 5,
"name": "Miscellaneous",
"image": "https://i.imgur.com/BG8J0Fj.jpg",
"creationAt": "2023-12-24T10:24:11.000Z",
"updatedAt": "2023-12-24T10:24:11.000Z"
}
}
view raw productsResp hosted with ❤ by GitHub

Now let’s see how we can use ‘Golang Testify’ and ‘Python Pytest’ to do the testing.

Test Naming Conventions

Golang Testing

According to Go Testing package, the test file must have _test.go suffix.
In function, each test function has following the naming structure func TestXXX(*testing.T) .

getProduct_test.go //Filename
func Test_GetProductDetailsSuccess(t *testing.T) {} //Test Function

Python Pytest

For Pytest, to create a new test suite, the test could be test_.py or _test.py .
Inside the test file, test functions start with the word test_ .

test_getProduct.py #Filename
def test_GetProductDetailsSuccess(self):{} #Test Function

Assertion

Golang Testify

The official documentation for both frameworks consists of two packages: require and assert .

  • Require

This implies that if the response result is not a pass, the subsequent testing will not proceed.

import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
var (
idNo = 50
productResponse Response
expectedId = 49
expectedTitle = "Bike"
)
require.Equal(t, expectedId, productResponse.ID) // Non-expected result
assert.Equal(t, expectedTitle, productResponse.Title) // Non-expected result
  • Assert

In contrast, the framework will continue with assertion testing even if the response result is not a pass.

import (
"github.com/stretchr/testify/assert"
"testing"
)
var (
idNo = 50
productResponse Response
expectedId = 49
expectedTitle = "Bike"
)
assert.Equal(t, expectedId, productResponse.ID) // Non-expected result
assert.Equal(t, expectedTitle, productResponse.Title) // Non-expected result

Testify offers a variety of assertion types. Let assertion testing can be more flexible.
Ref. https://pkg.go.dev/github.com/stretchr/testify/assert#pkg-index

Here’s an example of Golang Testify testing:

import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
var (
idNo = 50
productResponse Response
expectedTitle = "Practical Plastic Bike"
expectedPrice = 911
expectedDescriptionPattern = "New range of formal shirts.*apart"
expectedImages = []string{"https://i.imgur.com/0qQBkxX.jpg", "https://i.imgur.com/I5g1DoE.jpg", "https://i.imgur.com/myfFQBW.jpg"}
expectedCategoryId = 5
expectedCategoryName = "Miscellaneous"
expectedTime, err = time.Parse(time.RFC3339, "2023-12-24T10:24:11.000Z")
)
require.Equal(t, idNo, productResponse.ID)
assert.Equal(t, expectedTitle, productResponse.Title)
assert.NotZero(t, expectedPrice, productResponse.Price)
assert.GreaterOrEqual(t, expectedPrice, productResponse.Price)
assert.Regexpf(t, expectedDescriptionPattern, productResponse.Description, "DescriptionShouldMatchThePattern")
assert.ElementsMatch(t, expectedImages, productResponse.Images)
assert.Equal(t, expectedCategoryId, productResponse.Category.ID)
if productResponse.Category.Name != expectedCategoryName {
expectedError := errors.New("Expected error, but got nil")
assert.Error(t, expectedError)
}
creationTime, err := time.Parse(time.RFC3339, productResponse.CreationAt.String())
assert.True(t, creationTime.Before(expectedTime) || creationTime.Equal(expectedTime))

Python Pytest

Pytest utilizes the assert statement exclusively. Moreover, if the response result is not a pass, the testing process will not continue.

import json
import pytest
idNo = 50
expectedId = 49
expectedTitle = "Bike"
respJson = req.json()
assert respJson["id"] == expectedId # Non-expected result
assert respJson["title"] == expectedTitle # Non-expected result

Pytest has relatively fewer assertion types. However, additional libraries can be relied upon to fulfill specific testing needs.
Ref. https://docs.pytest.org/en/7.1.x/how-to/assert.html#

Here’s an example of Python Pytest testing:

import re
import json
import pytest
from datetime import datetime, timezone
idNo = 50
expectedTitle = "Practical Plastic Bike"
expectedPrice = 911
expectedDescriptionPattern = "New range of formal shirts.*apart"
expectedImages = ["https://i.imgur.com/0qQBkxX.jpg", "https://i.imgur.com/I5g1DoE.jpg", "https://i.imgur.com/myfFQBW.jpg"%5D
expectedCategoryId = 5
expectedCategoryName = "Miscellaneous"
expectedTime = datetime.strptime("2023-12-24T10:24:11.000Z", "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc)
respJson = req.json()
assert respJson["id"] == idNo
assert respJson["title"] == expectedTitle
assert respJson["price"] <= expectedPrice
assert bool(re.match(expectedDescriptionPattern, respJson["description"]))
assert set(respJson["images"]) == set(expectedImages)
assert respJson["category"]["id"] == expectedCategoryId
assert respJson["category"]["name"] == expectedCategoryName
resultCreationTime = datetime.strptime(respJson["creationAt"], "%Y-%m-%dT%H:%M:%S.%f%z")
assert resultCreationTime.timestamp() <= expectedTime.timestamp()
view raw PyPytestTesting hosted with ❤ by GitHub

Running Tests With Parallel

Both frameworks provide parallel testing functionality.

Golang Testify

Golang Testify offers its own parallel function, allowing users to specify the maximum number of cases for test execution.

$ go test -parallel 5 # 5 = maximum number of cases

Python Pytest

For Python Pytest, the pytest-xdist library needs to be installed to enable parallel testing.

$ pip install pytest-xdist
$ pytest -n 5 # 5 = maximum number of cases

After Testing

Generate Test Report

Following the completion of testing, certain libraries/packages support those frameworks for generating reports to visually represent the test results.

I will shows that using Allure Report to demonstrate the testing result.

Golang Testify

$ go install github.com/robotomize/go-allure/cmd/golurectl@latest
$ go test -json -cover ./…|golurectl -l -e -s -a -o ~/Downloads/test_report –allure-suite MySuite –allure-labels epic:my_epic,custom:value –allure-tags UNIT,GO-ALLURE –allure-layers UNIT
$ allure serve test_report​

Python Pytest

$ pytest –alluredir=test_report test/product/test_postProduct.py
$ allure serve test_report​
這張圖片的 alt 屬性值為空,它的檔案名稱為 1*ySPPGdql_MY0B0F75BzOVg.png

My Insight

In my thoughts, both frameworks actually share a lot in common and offer many extension, package, library to achieve the testing. For a beginner like me, to complete API test which shouldn’t to difficult to me.

However, if I were to highlight a difference, I believe Golang Testify edges slightly ahead in terms of assertions. After all, it provides require and various assert for testing without needing additional packages.

Conclusion

When selecting an automation framework, there is no one-size-fits-all solution. Just choose the testing framework that best suits your needs.

Lastly, the following list serves as inspiration for my article :