No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Implementando Mocks

21/30
Recursos

Aportes 8

Preguntas 6

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Para los que se preguntan porque hay tantos 鈥渆rrores鈥 (lo rojo) en el codigo como se ve en esta parte:

Es porque el language server (gopls) no sabe como relacionar main.go y main_test.go porque no hacen parte de un go module. La 鈥渟olucion鈥 mas sencilla es recargar el language server. Si no, solo creen un go module:

$ go mod init testingmod

Me quedaron algunas dudas respecto a este codigo, ya que no entedia muy bien como es que se reasignaban las funciones de 鈥淕etEmployeeById鈥 y 鈥淕etPersonByDNI鈥 si estaban fuera del scope de 鈥渕ockFunc鈥

As铆 que decid铆 preguntarle al chat GPT y me aclar贸 muchas dudas, les comparto mi conversaci贸n traducida por si a alguien le puede servir a aclarar un poco mas el tema


YO:

Estoy trabajando con el siguiente c贸digo en go
[le adjunt茅 todo nuestro codigo]
pero no entiendo muy bien lo que est谩 haciendo, 驴podr铆as explic谩rmelo?

GPT:

Este c贸digo es un caso de prueba para una funci贸n llamada GetFullTimeEmployeeById. El caso de prueba define una tabla de entradas y salidas esperadas de prueba, y luego recorre cada prueba en la tabla, ejecutando la funci贸n GetFullTimeEmployeeById con las entradas de la prueba y comparando su salida con la salida esperada.

Antes de ejecutar cada prueba, el c贸digo reasigna las funciones globales GetEmployeeById y GetPersonByDNI a funciones an贸nimas que devuelven valores codificados. Esto se hace para que la funci贸n GetFullTimeEmployeeById siempre devuelva la misma salida para una prueba dada, independientemente del estado actual del sistema.

El c贸digo tambi茅n comprueba si ocurri贸 un error durante la ejecuci贸n de la prueba, y si se encuentra un error, la prueba lo registra
Al final de la prueba, el c贸digo reasigna las funciones originales GetEmployeeById y GetPersonByDNI a las variables globales, para asegurarse de que la prueba no interfiera con otras partes del c贸digo que puedan estar utilizando estas funciones.

YO:

Gracias, pero todav铆a no entiendo c贸mo puede reasignar globalmente las funciones 鈥淕etEmployeeById鈥 y 鈥淕etPersonByDNI鈥 en el 鈥渕ockFunc鈥, 驴no se supone que el codigo reasigna las funciones s贸lo mientras est谩n dentro de las llaves de 鈥渕ockFunk鈥?

GPT:

Tienes raz贸n en que, por defecto, la reasignaci贸n de las funciones GetEmployeeById y GetPersonByDNI dentro de la funci贸n mockFunc s贸lo durar铆a lo que dure la llamada a la funci贸n. Sin embargo, dado que estas funciones son variables globales, la reasignaci贸n dentro de la funci贸n mockFunc est谩 cambiando efectivamente el valor de la variable global, y no s贸lo el valor de la variable local con el mismo nombre. Por lo tanto, los cambios en estas variables globales persisten fuera del 谩mbito de la funci贸n mockFunc.

Tambi茅n vale la pena se帽alar que, mientras reasigna las funciones globales dentro de mockFunc a funciones an贸nimas que devuelven valores codificados, tambi茅n almacena las funciones originales en variables denominadas originalGetEmployeeById y originalGetPersonByDNI respectivamente.
Esto se hace para que el c贸digo pueda restaurar las funciones originales una vez finalizado el caso de prueba, para evitar afectar al estado del sistema una vez finalizado el caso de prueba.

YO:

De acuerdo, ahora lo entiendo mejor, pero tengo otra pregunta, 驴por qu茅 las funciones almacenadas en las variables 鈥渙riginalGetEmployeeById鈥 y 鈥渙riginalGetPersonByDNI鈥 no se ven afectadas cuando 鈥渕ockFunc鈥 hace el cambio, si esas variables son las mismas funciones no se supone que deben cambiar tambi茅n?

GPT:

Tienes raz贸n en que originalGetEmployeeById y originalGetPersonByDNI almacenan los valores de las variables globales GetEmployeeById y GetPersonByDNI en el momento en que se asignan por primera vez. Sin embargo, cuando se llama a la funci贸n mockFunc y 茅sta reasigna las variables globales GetEmployeeById y GetPersonByDNI, no cambia los valores almacenados en las variables originalGetEmployeeById y originalGetPersonByDNI. Estas son dos variables separadas, con diferentes valores almacenados en ellas.

Es importante entender que las variables en Go se pasan por valor, lo que significa que cuando asignas una variable a otra, se almacena una copia del valor en la nueva variable, no una referencia al valor original. As铆, cuando a originalGetEmployeeById y originalGetPersonByDNI se les asigna el valor de GetEmployeeById y GetPersonByDNI, se les da una copia de los valores de esas variables, no una referencia a las variables mismas. Por lo tanto, cuando GetEmployeeById y GetPersonByDNI se reasignan en el mockFunc, esto no afecta a los valores almacenados en originalGetEmployeeById y originalGetPersonByDNI.

Hay una librer铆a llamada Mockery, que te genera los Mocks autom谩ticamente utilizando un comando.

Una vez te genera un mock, es llegar y utilizarlo. Muy f谩cil y practico.
Dejo el proyecto para que le echen una mirada:
https://github.com/vektra/mockery

package main

import "testing"

func TestGetFullTimeEmployeeById(t *testing.T) {
	table := []struct {
		id               int
		dni              string
		mockFunc         func()
		expectedEmployee FullTimeEmployee
	}{
		{
			id:  1,
			dni: "12345678",
			mockFunc: func() {
				GetEmployeeByID = func(id int) (Employee, error) {
					return Employee{
						Id:       1,
						Position: "CEO",
					}, nil
				}

				GetPersonByDNI = func(dni string) (Person, error) {
					return Person{
						Name: "John Doe",
						Age:  35,
						DNI:  "12345678",
					}, nil
				}
			},
			expectedEmployee: FullTimeEmployee{
				Person: Person{
					Age:  35,
					DNI:  "12345678",
					Name: "John Doe",
				},
				Employee: Employee{
					Id:       1,
					Position: "CEO",
				},
			},
		},
	}

	// Save the original functions
	originalGetEmployeeByID := GetEmployeeByID
	originalGetPersonByDNI := GetPersonByDNI

	for _, test := range table {
		// Set the mock functions for the test
		test.mockFunc()

		// Call the function to test
		ft, err := GetFullTimeEmployeeById(test.id, test.dni)
		if err != nil {
			t.Errorf("Error getting full time employee by id: %v", err)
		}

		// Check if the result is the expected
		if ft.Age != test.expectedEmployee.Age {
			t.Errorf("Expected age %v, got %v", test.expectedEmployee.Person.Age, ft.Person.Age)
		}

		if ft.DNI != test.expectedEmployee.DNI {
			t.Errorf("Expected dni %v, got %v", test.expectedEmployee.Person.DNI, ft.Person.DNI)
		}

		if ft.Name != test.expectedEmployee.Name {
			t.Errorf("Expected name %v, got %v", test.expectedEmployee.Person.Name, ft.Person.Name)
		}

		if ft.Employee.Id != test.expectedEmployee.Employee.Id {
			t.Errorf("Expected employee id %v, got %v", test.expectedEmployee.Employee.Id, ft.Employee.Id)
		}

		if ft.Employee.Position != test.expectedEmployee.Employee.Position {
			t.Errorf("Expected employee position %v, got %v", test.expectedEmployee.Employee.Position, ft.Employee.Position)
		}
	}

	// Restore the original functions
	GetEmployeeByID = originalGetEmployeeByID
	GetPersonByDNI = originalGetPersonByDNI
}

Hola gente, aqu铆 les dejo mis apuntes del Apartado de testing con golang, por si quieren revisarlo jeje
https://plump-flare-484.notion.site/Testing-c945f250509e449d8b9dee247bdc63a6

Tambi茅n se puede hacer uso de

reflect.DeepEqual()

Para comparar objetos sin tener que ir propiedad por propiedad, en este caso quedar铆a as铆 la 煤nica condicional:

if !reflect.DeepEqual(ft, test.expectedEmployee) {
	t.Errorf("Not equal employees, \nExpected: %v\nGot: %v" , ft , test.expectedEmployee)
}
Basicamente una manera de no esperar la ejecucion de una funcion para que te devuelva su valor si no devolver los valores de manera directa para ahorrar tiempo.

Se entiende el concepto de poder mockear, reemplazando la implementaci贸n y restaur谩ndola al final. Recomendar铆a evitar este m茅todo, dado que la implementaci贸n se puede reemplazar en un test, como en el programa original.

Adem谩s, el programador se puede olvidar f谩cilmente de hacer alguna restauraci贸n y traiga consecuencias inesperadas en el resultado de las pruebas unitarias.

En otras aportaciones se comentaron herramientas para generar mocks de forma automatizada, esto es una mejor soluci贸n. Agrego que es preferible evitar los mocks y los efectos secundarios cuando sea posible, hace que el comportamiento del c贸digo m谩s predecible.