Ícone do site Aprenda Golang

Testes unitários com Testify Suite e sqlmock

gray and brown mountain

Photo by Pixabay on Pexels.com

Alguns meses atrás, eu estava escrevendo uma aplicação. Quando fui escrever os testes do meu repositório, fiquei incomodado em ficar copiando e colando parte do código para gerar uma nova instância do repositório para cada ação do CRUD que eu queria testar. Foi então que, procurando na documentação do testify, descobri sobre o package suite.

Se você nunca utilizou o testify, recomendo que leia primeiro o nosso post “Como escrever testes com testify”.

Nesse post, vou mostrar como podemos escrever testes unitários utilizando o package testify suite e sqlmock.

Para que o post não fique muito extenso, ao invés de implementar o repositório também, vamos só imaginar que ele tem os métodos Insert, Update, Delete e FindByID implementados.

Iniciando o testify suite

Para começar, precisamos criar uma struct que faça o embed da struct suite.Suite. Esse embed é obrigatório. Todos os outros campos não são obrigatórios. São apenas uma forma de facilitar a nossa vida na hora da escrita dos testes.

type RepositorySuite struct {
	suite.Suite
	conn *sql.DB
	DB *gorm.DB
	mock sqlmock.Sqlmock

	repo *repository
	person *Person
}

Agora, vamos escrever o método SetupSuite. Esse método será executado antes de todos os outros. Nele, vamos adicionar os valores à todos os campos que criamos.

func (rs *RepositorySuite) SetupSuite() {
	var (
		err error
	)

	rs.conn, rs.mock, err = sqlmock.New()
	assert.NoError(rs.T(), err)

	dialector := postgres.New(postgres.Config{
		DSN: "sqlmock_db_0",
		DriverName: "postgres",
		Conn: rs.conn,
		PreferSimpleProtocol: true,
	})

	rs.DB, err = gorm.Open(dialector, &gorm.Config{})
	assert.NoError(rs.T(), err)

	rs.repo = NewRepository(rs.DB)
	assert.IsType(rs.T(), &repository{}, rs.repo)

	rs.person = &Person{ID: 1, Name: "Tiago Temporin", Age: 32, Document: "000.000.000-00"}
}

Na sequência, vamos implementar o método AfterTest. Embora não seja uma obrigatoriedade, implementando esse método, conseguimos garantir que nenhuma expectativa do sqlmock ficou sem ser atendida.

func (rs *RepositorySuite) AfterTest(_, _ string) {
	assert.NoError(rs.T(), rs.mock.ExpectationsWereMet())
}

Testando o repositório

Com o setup do testify suite pronto, vamos começar escrever os testes para os métodos do repositório. Para cada método que vimos acima, iremos implementar um método de teste com o prefixo Test + o nome do método que vamos testar*****.*****

// TESTA O MÉTODO INSERT
func (rs *RepositorySuite) TestInsert() {
	rs.mock.ExpectBegin()
	rs.mock.ExpectExec(
		regexp.QuoteMeta(`INSERT INTO "people" ("id", "name","age","document") VALUES ($1,$2,$3)`)).
		WithArgs(
			rs.person.ID,
			rs.person.Name,
			rs.person.Age,
			rs.person.Document).
		WillReturnResult(sqlmock.NewResult(1, 1))
	rs.mock.ExpectCommit()

	p, err := rs.repo.Insert(rs.person)
	assert.NoError(rs.T(), err)
	assert.Equal(rs.T(), rs.person, p)
}

// TESTA O MÉTODO UPDATE
func (rs *RepositorySuite) TestUpdate() {
	rs.person.Age = 33

	rs.mock.ExpectBegin()
	rs.mock.ExpectExec(
		regexp.QuoteMeta(`UPDATE "people" SET "name"=$1,"age"=$2,"document"=$3 WHERE "id" = $4`)).
		WithArgs(
			rs.person.Name,
			rs.person.Age,
			rs.person.Document,
			rs.person.ID).
		WillReturnResult(sqlmock.NewResult(1, 1))
	rs.mock.ExpectCommit()

	p, err := rs.repo.Update(rs.person)
	assert.NoError(rs.T(), err)
	assert.Equal(rs.T(), rs.person, p)
}

// TESTA O MÉTODO DELETE
func (rs *RepositorySuite) TestDelete() {
	rs.mock.ExpectBegin()
	rs.mock.ExpectExec(
		regexp.QuoteMeta(`DELETE FROM "people" WHERE "people"."id" = $1`)).
		WithArgs(rs.person.ID).
		WillReturnResult(sqlmock.NewResult(0, 1))
	rs.mock.ExpectCommit()

	err := rs.repo.Delete(rs.person.ID)
	assert.NoError(rs.T(), err)
}

// TESTA O MÉTODO FIND
func (rs *RepositorySuite) TestFindyByID() {
	rows := sqlmock.NewRows([]string{"id", "name", "age", "document"}).
		AddRow(
			rs.person.ID,
			rs.person.Name,
			rs.person.Age,
			rs.person.Document)

	rs.mock.ExpectQuery(
		regexp.QuoteMeta(`SELECT * FROM "people" WHERE "people"."id" = $1`)).
		WithArgs().
		WillReturnRows(rows)

	p, err := rs.repo.FindByID(rs.person.ID)
	assert.NoError(rs.T(), err)
	assert.Equal(rs.T(), rs.person, p)
}

Executando testify suite

Para finalizar, precisamos escrever uma função de testes do próprio Go. Nela, vamos chamar a função suite.Run para poder executar os testes que escrevemos.

func TestSuite(t *testing.T) {
	suite.Run(t, new(RepositorySuite))
}

Pronto. Agora é só executar o go test para que nossa suite de testes seja executada.

Qualquer dúvida, é só deixar nos comentários.

Até a próxima!


Faça parte da comunidade!

Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.

Livros Recomendados

Abaixo listei alguns dos melhores livros que já li sobre GO.

Sair da versão mobile