Dependency Inyection y TDD en Golang

Victor Paredes
codeAndreani
Published in
3 min readFeb 15, 2022

Cuando hablamos de programación orientada a objetos y Golang, podemos decir que estamos hablando del mismo caso del gato de Schrödinger. Golang es un lenguaje orientado a objetos y a al mismo tiempo no lo es… 👀

Pero no vamos a vamos a filosofar sobre POO+Golang, mecánica cuántica, paradojas ni sobre la interpretacion de Copenhague… Si no vamos a ver cómo podemos implementar TDD utilizando mocks/fakes mediante la inyección de dependencias.

Inyección de dependencias en GO

Me incomoda que no es como NodeJS (donde no lo necesitamos) y tampoco es como C#, que es un lenguaje completamente orientado a objetos. Sin embargo, la forma en que se resuelve funciona muy bien y queda muy prolijo y escalable.

La forma de realizar la inyección de componentes es mediante el uso de interfaces, structures y métodos. Veamos un poco de código:

type InterfaceObjeto interface {
HazAlgo()
}
type ObjetoUno struct {
objetoInyectado SomeObjectType
}
func (o ObjetoUno) HazAlgo() {
// Codigo
o.objetoInyectado.AnotherMethod()
}

Fácil ¿verdad? Pues no hay mucho más que esto, en GO las cosas son bastante simples. En el ejemplo vemos tres componentes.

  • Interface llamada InterfaceObjeto que es la que vamos a utilizar por si necesitamos mockear esta structure. Simplemente en nuestros test creamos un mock/fake que cumpla con la interface y listo.
  • Structure ObjetoUno que es el objeto que vamos a utilizar para satisfacer a la interfaz mencionada previamente. En este objeto tenemos una propiedad llamada objetoInyectado que corresponde a la dependencia que vamos a inyectar.
  • El método HazAlgo lo estamos agregando a la structure ObjetoUno como una propiedad. Dentro de esta propiedad tenemos visibilidad de todas las propiedades de ObjetoUno mediante la variable o.

Veamos un ejemplo que sea mas proximo a la vida real.

type BaseDeDatos interface {
RunQuery(query string)
}
type Servicio struct {
datos BaseDeDatos
}
func (s Servicio) Borrar(id string) {
o.datos.RunQuery("delete from tabla where id = " + id)
}

¡Advertencia! Nunca ejecutes una query de esta forma ya que permitís inyección de SQL en tu código. Tomá esto solo como un ejemplo.

En el código anterior vemos como estamos inyectando un motor de base de datos a un servicio que va a ejecutar una consulta. Servicio tiene una propiedad, que es una interfaz, llamada BaseDedatos la cual al estar en una propiedad nosotros podemos reemplazarla por cualquier structure que cumpla con la interfaz BaseDeDatos. De esta forma podemos cambiarlo por una de SqlServer, MySql, Postgresql u Oracle.

Ahora, agregamos un poco más de pimienta en esta receta y hacemos un constructor:

func NewServicio() *Servicio {
servicio := Servicio{}
servicio.datos = {colocar aqui a implementacion PROD de su DB

return servicio
}

De esta forma, cuando llamemos a NewServicio en nuestro código estaremos creando un Servicio que tendrá la base de datos productiva.

Por otro lado, si estuviéramos creando pruebas unitarias de este servicio deberíamos crearlo de esta forma:

t.Run("test descripcion", func(t *testing.T) {
// Arrange
target = servicio{}
target.datos = {coloca aqui el mock de tu DB}
// Act
result := target.Borrar("123")
// Assert
assert.Equal(t, ...)
})

En lugar de llamar a NewServicio() creamos la structure a mano y le inyectamos nuestro mock de base de datos (para cumplir el principio FIRST)

Como siempre, si tienen dudas, consultas o solamente quieren charlar sobre algo me pueden dejar un mensaje aquí o en Github y hablando de eso, les dejo un repo con una implementación para que jueguen en sus casas.

git clone https://github.com/victorparedes/golang_di_tdd

--

--