Andres Alfonso Puello Chavez
EstudianteMaría Camila Lenis Restrepo
ProfesorSebastian Villegas
EstudianteOscar Pinochet
EstudianteSteven Javier Arevalo Poveda
EstudianteLeonardo Valdivieso
EstudianteMaría Camila Lenis Restrepo
ProfesorSteven Javier Arevalo Poveda
EstudianteRubens A. Rangel Gomez
EstudianteDavid Hernandez
EstudianteMi forma de probarlo sin usar los archivos .json y utilizando assert de testify
package utils import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" ) var ( pokemonFromApi = []byte(`{"id": 132,"name": "ditto","types": [{"slot": 1,"type": {"name": "normal"}}],"stats": [{"base_stat": 48,"stat": {"name": "hp"}},{"base_stat": 48,"stat": {"name": "attack"}},{"base_stat": 48,"stat": {"name": "defense"}},{"base_stat": 48,"stat": {"name": "special-attack"}},{"base_stat": 48,"stat": {"name": "special-defense"}},{"base_stat": 48,"stat": {"name": "speed"}}]}`) pokemonResponseDTO = []byte(`{"Id":132,"Name":"Ditto","Type": "Normal","Stats":{"Hp": 48,"Attack": 48,"Defense": 48,"Speed": 48}}`) ) func TestNewParsePokemonToDTO(t *testing.T) { apiPokemon := entities.PokemonApiResponse{} err := json.Unmarshal(pokemonFromApi, &apiPokemon) assert.NoError(t, err) parsePokemon, err := ParsePokemonToDTO(apiPokemon) assert.NoError(t, err) pokemonResponse := dto.Pokemon{} err = json.Unmarshal(pokemonResponseDTO, &pokemonResponse) assert.NoError(t, err) assert.Equal(t, parsePokemon, pokemonResponse) }
Genial Andrés. Es otra forma de resolverlo
Usualmente se separa en archivos json para mayor legibilidad y no mezclar cosas.
dos cosas las función ReadFile ahora esta en el package os y la función ya devuelve un []byte, por lo que no es necesario hacer []byte(body)
{ "Id": 1, "Name": "bulbasaur", "Type": "grass", "Abilities": { "Attack": 49, "Defense": 49, "Hp": 45, "Speed": 45 } }
Para obtener la respuesta de la pokeapi, no era mejor ir a la url de la pokeapi y consultarla directamente?
En este caso estamos usando mocks. Quiere decir que no dependeremos de que la pokeapi esté disponible, sino que veremos cómo el código reacciona a los escenarios que planteamos (cuando es success, cuándo no lo encuentra, etc). Por eso tenemos la respuesta ya guardada, para no llamar directamente. En otro tipo de pruebas, podría ser valioso hacerlo
{ "id": 1, "name": "bulbasaur", "types": [ { "slot": 1, "type": { "name": "grass" } }, { "slot": 2, "type": { "name": "poison" } } ], "stats": [ { "base_stat": 45, "stat": { "name": "hp" } }, { "base_stat": 49, "stat": { "name": "attack" } }, { "base_stat": 49, "stat": { "name": "defense" } }, { "base_stat": 65, "stat": { "name": "special-attack" } }, { "base_stat": 65, "stat": { "name": "special-defense" } }, { "base_stat": 45, "stat": { "name": "speed" } } ] }
🧠 Idea principal
En testing no siempre se deben hacer llamadas reales a APIs externas; es mejor simular respuestas (mocks) para tener pruebas controladas, repetibles y enfocadas en el comportamiento del código.
🧩 Fundamentos
1. Uso de mocks en APIs
2. Aislamiento del test
3. Uso de datos mockeados
.json (más legibles y organizados)4. Reutilización de código en tests
5. Uso de assertions (testify)
assert.NoError)assert.Equal)6. Transformación de datos (parser)
🔑 Puntos importantes
🎯 Conclusión corta
Testear no es probar el mundo real, es controlar un entorno simulado para validar tu lógica.
Agrego mi forma de probarlo juntando con todos los cursos para saber su opiniones si se puede ejecutar una función para no repetir el mismo codigo.
Mi archivo parser_test.go
package util import ( "catching-pokemons/models" "encoding/json" "io/ioutil" "testing" "github.com/stretchr/testify/assert" ) func loadJsonToMap(pathFile string, data interface{}) error { body, err := ioutil.ReadFile(pathFile) if err != nil { return err } err = json.Unmarshal([]byte(body), &data) if err != nil { return err } return nil } func TestParsePokemon(t *testing.T) { var apiPokemon map[string]models.PokeApiPokemonResponse err := loadJsonToMap("samples/pokeapi_response.json", &apiPokemon) assert.NoError(t, err, "Error loading pokeapi_response file") var pokemon map[string]models.Pokemon err = loadJsonToMap("samples/api_response.json", &pokemon) assert.NoError(t, err, "Error loading api_response file") type args struct { apiPokemon models.PokeApiPokemonResponse } tests := []struct { name string args args want models.Pokemon wantErr error }{ { name: "Success pikachu", args: args{apiPokemon: apiPokemon["pikachu"]}, want: pokemon["pikachu"], wantErr: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParsePokemon(tt.args.apiPokemon) assert.NoError(t, err, "ParsePokemon() error = %v, wantErr %v", err, tt.wantErr) assert.Equal(t, tt.want, got, "ParsePokemon() = %v, want %v", got, tt.want) }) } }
Mi archivo api_response.json
{ "pikachu": { "Id": 25, "Name": "pikachu", "Type": "electric", "Abilities": { "Attack": 55, "Defense": 40, "Hp": 35, "Speed": 90 } }, "bulbasaur": { "Id": 1, "Name": "bulbasaur", "Type": "grass", "Abilities": { "Attack": 49, "Defense": 49, "Hp": 45, "Speed": 45 } }, "charmander": { "Id": 4, "Name": "charmander", "Type": "fire", "Abilities": { "Attack": 52, "Defense": 43, "Hp": 39, "Speed": 65 } }, "squirtle": { "Id": 7, "Name": "squirtle", "Type": "water", "Abilities": { "Attack": 48, "Defense": 65, "Hp": 44, "Speed": 43 } } }
Mi archvivo pokeapi_response.json
{ "pikachu" : { "id": 25, "name": "pikachu", "types": [ { "slot": 1, "type": { "name": "electric" } } ], "stats": [ { "base_stat": 35, "stat": { "name": "hp" } }, { "base_stat": 55, "stat": { "name": "attack" } }, { "base_stat": 40, "stat": { "name": "defense" } }, { "base_stat": 50, "stat": { "name": "special-attack" } }, { "base_stat": 50, "stat": { "name": "special-defense" } }, { "base_stat": 90, "stat": { "name": "speed" } } ] }, "bulbasaur": { "id": 1, "name": "bulbasaur", "types": [ { "slot": 1, "type": { "name": "grass" } }, { "slot": 2, "type": { "name": "poison" } } ], "stats": [ { "base_stat": 45, "stat": { "name": "hp" } }, { "base_stat": 49, "stat": { "name": "attack" } }, { "base_stat": 49, "stat": { "name": "defense" } }, { "base_stat": 65, "stat": { "name": "special-attack" } }, { "base_stat": 65, "stat": { "name": "special-defense" } }, { "base_stat": 45, "stat": { "name": "speed" } } ] }, "charmander": { "id": 4, "name": "charmander", "types": [ { "slot": 1, "type": { "name": "fire" } } ], "stats": [ { "base_stat": 39, "stat": { "name": "hp" } }, { "base_stat": 52, "stat": { "name": "attack" } }, { "base_stat": 43, "stat": { "name": "defense" } }, { "base_stat": 60, "stat": { "name": "special-attack" } }, { "base_stat": 50, "stat": { "name": "special-defense" } }, { "base_stat": 65, "stat": { "name": "speed" } } ] }, "squirtle": { "id": 7, "name": "squirtle", "types": [ { "slot": 1, "type": { "name": "water" } } ], "stats": [ { "base_stat": 44, "stat": { "name": "hp" } }, { "base_stat": 48, "stat": { "name": "attack" } }, { "base_stat": 65, "stat": { "name": "defense" } }, { "base_stat": 50, "stat": { "name": "special-attack" } }, { "base_stat": 64, "stat": { "name": "special-defense" } }, { "base_stat": 43, "stat": { "name": "speed" } } ] } }