gray and brown mountain

Testes unitários com Testify Suite e sqlmock

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!


Se inscreva na nossa newsletter

* indicates required

Deixe uma resposta