> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/testing-standards/writing-tests.md).

# Escribir pruebas

Las pruebas se pueden escribir de varias maneras. Esta sección intenta dar un estándar y una justificación detrás de la forma en que escribimos las pruebas.

Las pruebas DEBEN escribirse usando el **describe** y el **pruebas** métodos proporcionados por jest

## Descripción y construcción del contexto

El **describe** El método DEBERÍA usarse para describir, siempre que sea posible, el contexto en el que se ejecutará la parte de código que se va a probar y DEBERÍA construir ese contexto dentro de su ámbito.

```tsx
describe('cuando la bandera es roja', () => {
	...
})

describe('cuando la bandera es azul', () => {
	...
})
```

Para describir el contexto, DEBERÍAMOS usar una de las siguientes palabras: **cuando** para los describe de nivel superior y **teniendo** o **y** para indicar que hay un contexto que depende de otro contexto que se definió antes. La justificación detrás de esto es tener contextos autodescriptivos que puedan entenderse y comprobarse fácilmente combinando las frases de describe. Como la idea es concatenar las descripciones de los contextos, **describe** DEBE escribirse en minúsculas.

```tsx
describe('cuando la bandera es roja', () => {
	...
	describe('y el viento es fuerte', () => {
     // El contexto aquí se describe por la conjunción de los describe:
     // "cuando la bandera roja es roja y el viento es fuerte"
     ...
  })
})
```

Se pueden anidar múltiples describe en el mismo nivel y en un nivel inferior. Los describe del mismo nivel denotan contextos diferentes y los describe de niveles inferiores (describe anidados) denotan un contexto más específico.

```tsx
describe('cuando la bandera es roja', () => {
	...
	describe('y el viento es fuerte', () => {
     ...
  })

	describe('y el viento es débil', () => {
     ...
  })
})
```

Como se mencionó antes, cada describe DEBERÍA ir acompañado de un método que construya el contexto o de un método que destruya o cambie el contexto. Los métodos que DEBEN usarse son los **beforeEach** y el **afterEach**.

{% hint style="warning" %}
El framework de Jest tiene dos métodos, **beforeEach** y **beforeAll**, pero combinarlos genera confusión y problemas de ejecución cuando hay contextos anidados, ya que [el orden en que se ejecutan](https://jestjs.io/docs/setup-teardown#scoping) no es lo que esperaríamos.
{% endhint %}

Los contextos creados con el **beforeEach** método estarán cada vez más definidos a medida que profundicemos en el árbol de describe. Cada **beforeEach** especificará más el contexto según lo que diga su texto.

### Alcance de variables

El **variables** establecidas en los contextos DEBERÍAN definirse según el ámbito en el que se van a usar. Es decir, si una variable se usa solo para un contexto específico en un nivel anidado de describe, la variable DEBERÍA definirse solo en ese contexto específico. La justificación detrás de esto es mantener las variables cerca del código en el que se usan, haciendo que el código sea más fácil de entender.

```tsx
let flag = 'blue'
describe('cuando la bandera es roja', () => {
	let wind = 'mild'
	beforeEach(() => {
    flag = 'red'
  })
  ...

	describe('y el viento es fuerte', () => {
		beforeEach(() => {
	    wind = 'strong'
	  })
    ...
  })

	describe('y el viento es débil', () => {
		beforeEach(() => {
	    wind = 'weak'
	  })
    ...
  })
})
```

Variables de **tipos primitivos**, que **no se cambiarán** en el contexto en el que se definen o en contextos posteriores DEBERÍAN definirse como **const** una sola vez, en la **beforeEach** definición en el ámbito.

Variables de **tipos primitivos** que se cambiarán en el contexto en el que se definen o en contextos posteriores DEBEN definirse como **let**, ya que la naturaleza de TypeScript lo exige.

Variables de **tipos no primitivos** (objetos, arrays, etc.) DEBEN definirse como **let** y su valor DEBE establecerse en un **beforeEach.** La justificación detrás de esto es que los objetos en JS son mutables, lo que significa que cualquier ejecución de código que use ese objeto está sujeta a cambios y puede afectar a otras pruebas de manera involuntaria.

```tsx
let flag
describe('cuando la bandera es roja', () => {
  // Un valor primitivo que se va a modificar en múltiples ámbitos.
	let wind
  // Un valor de objeto que se va a usar en el código de la prueba.
  let someObject
	beforeEach(() => {
    flag = 'red'
		someObject = { id: 1 }
  })
  ...

	describe('y el viento es fuerte', () => {
		// Un valor primitivo constante que se va a usar solo en este ámbito.
		const aConstantPrimitiveValue = 'algo'
		beforeEach(() => {
	    wind = 'strong'
	  })
    ...
  })

	describe('y el viento es débil', () => {
		beforeEach(() => {
	    wind = 'weak'
	  })
    ...
  })
})
...
```

Las variables DEBERÍAN estar correctamente tipadas siempre que sea posible. Los tipos repetidos DEBERÍAN abstraerse en un tipo.

```tsx
let flag: string
describe('cuando la bandera es roja', () => {
  // Un valor primitivo que se va a modificar en múltiples ámbitos.
	let wind: string
  // Un valor de objeto que se va a usar en el código de la prueba.
  let someObject: { id: string }
	beforeEach(() => {
    flag = 'red'
		someObject = { id: 1 }
  })
  ...

	describe('y el viento es fuerte', () => {
		// Un valor primitivo constante que se va a usar solo en este ámbito.
		const aConstantPrimitiveValue = 'algo'
		beforeEach(() => {
	    wind = 'strong'
	  })
    ...
  })

	describe('y el viento es débil', () => {
		beforeEach(() => {
	    wind = 'weak'
	  })
    ...
  })
})
...
```

## Describir expectativas y ejecutar código

El **pruebas** El método DEBE colocarse siempre dentro del ámbito de un **describe** y DEBE usarse para describir qué es lo que esperamos del código que vamos a probar y DEBERÍA contener, si es posible, solo **una** aserción. Pueden existir múltiples aserciones por **pruebas** puede haber si hay un problema de rendimiento, ya que las pruebas se ejecutarán una vez por **pruebas** (por culpa del **beforeEach**), o si lo que se espera puede describirse con claridad en la descripción de la expectativa.

```tsx
let flag: string
describe('cuando la bandera es roja', () => {
	let wind: string
  let someObject: { id: string }
	beforeEach(() => {
    flag = 'red'
		someObject = { id: 1, swim: jest.fn() }
  })
  ...

  // Una única expectativa de la ejecución de una prueba en un contexto (un it)
	describe('y el viento es fuerte', () => {
		const aConstantPrimitiveValue = 'algo'
		beforeEach(() => {
	    wind = 'strong'
	  })
    
		it('no debería ir a nadar', () => {
      expect(goSwimming(flag, wind, someObject)).toBe(false)
    })
  })

  // Múltiples expectativas de la ejecución de una prueba en un contexto (dos it)
	describe('y el viento es débil', () => {
    let result: boolean
		beforeEach(() => {
	    wind = 'weak'
      result = goSwimming(flag, wind, someObject)
	  })

		it('debería ir a nadar', () => {
      expect(goSwimming(flag, wind, someObject)).toBe(true)
    })

    it('debería haber llamado al método swim', () => {
      expect(someObject.swim).toHaveBeenCalled()
    })
  })
})
...
```

La justificación detrás de esta estructura es proporcionar claridad al revisor de las pruebas y a los desarrolladores que mantendrán y harán cambios al código, ya que cada contexto y expectativa se enumera claramente, lo que facilita entender qué se está probando, cómo se prueba y qué queda por probar.

Siguiendo las **describe** descripciones hasta el **pruebas**los desarrolladores pueden entender fácilmente qué se está probando concatenando las frases.

```tsx
// Esto se lee como: "cuando la bandera es roja y el viento es fuerte, 
// debería ir a nadar"
describe('cuando la bandera es roja', () => {
  ...
  describe('y el viento es fuerte', () => {
		...
		it('debería ir a nadar', () => {
			...
		})
  })
})
```

### Escribir expectativas claras

La descripción de las expectativas escritas en los **pruebas**deben ser lo más descriptivas posible sobre lo que se espera de la ejecución de las pruebas. Los desarrolladores NO DEBEN usar frases abstractas o generales para definir expectativas, ya que esto quita claridad sobre lo que se espera del código.

{% hint style="danger" %}
**El desarrollador NO DEBE usar frases como:**

* "debería funcionar como se espera" ⇒ ¿Qué es lo que debería funcionar como se espera?
* "debería devolver el valor correcto" ⇒ ¿Cuál es el valor correcto?
* "debería resolverse/devolverse/funcionar correctamente" ⇒ ¿Cómo funciona algo correctamente?
* "debería fallar" ⇒ ¿Cómo debería fallar? ¿Cuál es el mensaje que debería proporcionar?
  {% endhint %}

Debe tenerse en cuenta que al especificar lo que se espera en las **pruebas**o cuál será el contexto en las **describe**los desarrolladores PUEDEN usar nombres de funciones o mensajes de error exactos si es necesario para entender mejor la intención, pero DEBERÍAN usar una representación textual cuando sea posible para hacer que las pruebas sean más fáciles de mantener.

```tsx
// math.ts
export function div(a: number, b: number): number {
	if(b === 0)	{
		throw new Error('El divisor b es igual a 0, la división no se puede realizar')
	}
}

// test.spec.ts
import { div } from './math.ts'

describe('cuando se divide por cero', () => {
  // La descripción del it describe la intención del mensaje de la excepción
	it('debería lanzar una excepción que señale que una división por 0 no es posible', () => {
		expect(() => div(12, 0)).toThrowError('El divisor b es igual a 0, la división no se puede realizar')
	})
})
```

## Qué probar

Elegir qué probar puede variar dependiendo del código que se está ejecutando. Diferentes factores, desde el rendimiento o un gran dominio de entradas, pueden cambiar definitivamente lo que debería o no debería probarse en las pruebas. Aquí expondremos un único conjunto de casos que el desarrollador DEBERÍA probar si es posible.

```tsx
function run(kilometers: number): number {
	if(kilometers > 1000) {
    throw new Error('El corredor no puede correr más de 1000 kilómetros')
  }

  if(kilometers <= 10) {
		return kilometers
  } else if(kilometers > 10) {
		doSomething(kilometers)
    return beLazy(kilometers)
  }
}
```

La función run aquí tiene un par de flujos de ejecución diferentes. Las funciones, o secciones de código, NO DEBERÍAN probarse basándose solo en los diferentes flujos de ejecución posibles, sino en lo que se espera de la función, ya que la función podría no estar haciendo para lo que fue escrita y las pruebas podrían estar fuertemente acopladas al código y no detectar diferentes problemas en él. Esto implica que el desarrollador DEBERÍA probar siempre todas las rutas de ejecución posibles y también DEBERÍA probar otras posibles rutas de acuerdo con la semántica de la función.

Para este caso específico, el desarrollador DEBERÍA probar **al menos** los siguientes casos:

1. Ejecutar la función run con una cantidad de kilómetros mayor que 1000
2. Ejecutar la función run con una cantidad de kilómetros igual a 10
3. Ejecutar la función run con una cantidad de kilómetros mayor que 10 pero menor que 1000

Para el primer caso, el desarrollador necesita probar que la función run lanza una excepción. **Las excepciones DEBEN comprobarse por su mensaje de error**, ya que también podría lanzarse cualquier otra excepción, invalidando el propósito de la prueba. Si la excepción es personalizada, el desarrollador PUEDE comprobar la instancia de la excepción.

Para el segundo caso, el desarrollador necesita probar que la función devolvió el mismo número de kilómetros que se le dio.

Para el tercer caso, el desarrollador necesita probar que la función devolvió lo que una función externa, **beLazy** devolvió y, como **doSomething** (también ejecutada en el método) no puede comprobarse a través del valor devuelto por la función, el desarrollador DEBERÍA **probar que se llamó con los argumentos correctos**.

```tsx
import { doSomething, beLazy } from '../runningUtils'
jest.mock('../runningUtils')

// Nota que no hay un describe principal
// El único contexto global son las funciones que queremos simular con jest

const mockDoSomething = doSomething as jest.MockedFunction<typeof doSomething>
const mockBeLazy = beLazy as jest.MockedFunction<typeof beLazy>

describe('cuando se corren más de 1000 kilómetros', () => {
	it('debería lanzar un error que señale que el corredor no puede correr más de 1000 kilómetros', () => {
		expect(() => run(1001)).toThrowError("El corredor no puede correr más de 1000 kilómetros")
	})
})

describe('cuando se corren menos o igual que 10', () => {
	it('debería devolver la misma cantidad de kilómetros que la dada', () => {
		expect(run(2)).toEqual(2)
	})
})

describe('cuando se corren más de 10 kilómetros pero menos de 1000', () => {
	const kilometers = 50
	let result: number
	beforeEach(() => {
		mockDoSomething.mockReturnValueOnce(undefined)
		mockBeLazy.mockImplementationOnce(value => value)
		result = run(kilometers)
	})
	
	it('debería devolver los kilómetros después de ser perezoso', () => {
		expect(result).toEqual(kilometers)
	})

	it('debería llamar a la función doSomething con los kilómetros dados', () => {
		expect(mockDoSomething).toHaveBeenCalledWith(kilometers)
	})
})
```

## Cuándo simular

La simulación dependerá principalmente del tipo de pruebas que el desarrollador esté escribiendo.

* Las pruebas unitarias DEBERÍAN tener simuladas todas las funciones externas, con la excepción de funciones que hacen cosas simples, como formatear cadenas, etc.
* Las pruebas de API DEBERÍAN tener mocks solo para comunicarse con servicios externos, es decir, DBs u otras APIs. Si una de las operaciones que se realiza en la prueba afecta al rendimiento de la suite de pruebas, se podría implementar un mock para mitigar este problema.

Todos los mocks DEBEN hacerse usando las herramientas proporcionadas por Jest, con la excepción de los `redux-saga-test-plan` mocks.

### Mocks y funciones de utilidad

* Cualquier mock de Jest DEBERÍA simularse usando su **una sola vez** método cuando sea posible, es decir, `mockReturnValueOnce` o `mockResolvedValueOnce` no se recomienda `mockReturnValue` o `mockResolvedValue`. La justificación detrás de esto es evitar que se ejecuten mocks no deseados, cambiando la ejecución de nuestras pruebas.
* Jest ofrece un conjunto de Types que puedes usar para diferentes tipos de mocks. Por ejemplo, al simular un objeto, DEBERÍAS usar `jest.Mocked<typeof someObject>`, para clases DEBERÍAS usar `jest.MockedClass<typeof SomeClass>`, y para funciones, `jest.MockedFunction<typeof someFunction>`.
* Un `afterEach` DEBERÍA escribirse para ejecutarse después de cada prueba con un `jest.resetAllMocks` para limpiar cualquier implementación simulada de módulos simulados globalmente que podría filtrarse a otras pruebas. Puedes ignorar este restablecimiento si estás usando `mockReturnValueOnce` o `mockResolvedValueOnce` porque se hace automáticamente por ti.
* Se puede crear un directorio llamado `mocks` para almacenar mocks, que deberían colocarse en el directorio `test` o `spec` Los mocks grandes DEBEN almacenarse en archivos distintos de las pruebas para evitar archivos de prueba extensos. Los mocks pequeños, si no son muchos, DEBERÍAN mantenerse en las pruebas.
  * Los archivos de mock DEBERÍAN nombrarse como la entidad que están simulando, así que, por ejemplo, si estamos simulando un profile, el mock debería colocarse en el `/test/mocks/profile.ts` archivo.
  * En caso de que se requieran varios mocks grandes, DEBERÍAN colocarse en archivos distintos dentro de un directorio llamado como la entidad a simular, así que, por ejemplo, si tenemos dos mocks de profile, los almacenaremos como: `/test/mocks/profile/profile-with-wearables.ts` y `/test/mocks/profile/profile-without-wearables.ts`. Para acceder fácilmente a ellos, DEBERÍAS crear un `index.ts` archivo dentro del `/test/mocks/profile/` directorio y exportarlos.
  * Para evitar la mutación de objetos simulados, los mocks **DEBEN exportarse como funciones** que los devuelvan.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/testing-standards/writing-tests.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
