Talking About TestingATT</>
JavaScript para QAs

Obtén 100% en el cuestionario para continuar

Lección
Vista previa gratuita

Tipos de datos

Los primitivos (undefined, null, Boolean, Number, String) y los tipos estructurales (Array, Object, Function) con los que se construye toda prueba.

Todo lo que hay en una prueba es un dato. El nombre que escribes en un campo, el código de estado que volvió, la lista de filas de una tabla, la respuesta de sí o no a "¿este botón está visible?".

JavaScript ordena todo eso en una cantidad pequeña de tipos. Aprende los tipos y la mayoría de las sorpresas del lenguaje dejan de ser sorpresas.

Hay dos familias: primitivos y tipos estructurales.

Primitivos

Un primitivo es un valor único y simple. Hay cinco que vas a usar todo el tiempo.

String

Texto. Se escribe entre comillas simples, comillas dobles o backticks.

console.log('Acme Corporation')
console.log("Acme Corporation")

Elige un estilo de comillas y mantente consistente. Este curso usa comillas simples.

Number

Cualquier número. JavaScript no separa los números enteros de los decimales: 42 y 3.14 son ambos simplemente Number.

console.log(200)
console.log(19.99)
👨‍🏫 Por eso un código de estado y un precio son del mismo tipo. También significa que 0.1 + 0.2 te da 0.30000000000000004, que no es un bug de JavaScript sino la forma en que las computadoras guardan decimales en todas partes. Nunca hagas aserciones sobre el resultado exacto de una cuenta con decimales.

Boolean

Exactamente dos valores posibles: true y false. Este es el tipo que en el fondo produce toda aserción.

console.log(true)
console.log(false)

undefined

El valor de algo que existe pero al que nunca se le dio un valor. JavaScript te lo asigna solo.

let statusCode
console.log(statusCode)

Eso imprime undefined. Nadie le asignó nada.

null

El valor de algo definido deliberadamente como "nada". La diferencia con undefined importa: undefined significa "nadie llenó esto", null significa "alguien llenó esto con nada".

const middleName = null
console.log(middleName)
👨‍🏫 En una respuesta de API, "phone": null significa que el campo existe y que el cliente no tiene teléfono. Una clave phone que falta te da undefined, lo que significa que la API no envió el campo en absoluto. Esos son dos bugs distintos, así que vale la pena poder distinguirlos.

Tipos estructurales

Un tipo estructural guarda varios valores, en una estructura. Hay tres que necesitas.

Array

Una lista ordenada. Se escribe con corchetes y los ítems separados por comas. El orden se preserva, y las posiciones se numeran desde cero.

const sizes = ['Small', 'Medium', 'Large']

console.log(sizes[0])
console.log(sizes[2])
console.log(sizes.length)

Eso imprime Small, después Large, después 3.

👨‍🏫 Contar desde cero confunde a todo el mundo al principio. El primer ítem está en el índice 0, así que el último ítem de un array de tres ítems está en el índice 2, nunca en el 3. Pedir sizes[3] te da undefined.

Un array puede guardar cualquier cosa, incluidos otros arrays y objetos. En pruebas, un array suele ser una lista de filas, una lista de entradas o una lista de valores esperados.

Object

Una colección de valores con nombre. Se escribe con llaves, como pares clave: valor.

const customer = {
  name: 'Acme Corporation',
  size: 'Large',
  active: true,
}

console.log(customer.name)
console.log(customer.active)

Eso imprime Acme Corporation, después true.

Un objeto es lo que parece el cuerpo de una respuesta de API, y lo que parece un dato de prueba. Los arrays y los objetos se anidan libremente unos dentro de otros: un array de objetos de cliente es la forma más común que te vas a encontrar.

const customers = [
  { name: 'Acme Corporation', size: 'Large' },
  { name: 'Globex', size: 'Small' },
]

console.log(customers[1].name)

Eso imprime Globex.

Function

Un bloque de código reutilizable. En JavaScript una función es un valor como cualquier otro, y por eso pertenece a esta lista y no a un capítulo aparte.

function greet(name) {
  return 'Hola, ' + name
}

console.log(greet('Walmyr'))

Eso imprime Hola, Walmyr.

👨‍🏫 El hecho de que una función sea un valor es la idea más importante de este curso. Es lo que te permite pasarle una función al it(), al beforeEach() y al forEach(). La lección 7 cubre las funciones como corresponde.

Cómo verificar un tipo

typeof te dice de qué tipo es un valor.

console.log(typeof 'Acme')
console.log(typeof 42)
console.log(typeof true)
console.log(typeof undefined)
console.log(typeof { name: 'Acme' })
console.log(typeof [1, 2, 3])
console.log(typeof null)

Vale la pena quedarse mirando los dos últimos. typeof [1, 2, 3] dice 'object', no 'array', y typeof null también dice 'object', que es un error famoso del lenguaje que nunca se puede arreglar sin romper la web.

Para verificar si algo es un array, usa Array.isArray().

console.log(Array.isArray([1, 2, 3]))
console.log(Array.isArray({ name: 'Acme' }))

Eso imprime true, después false.

Resumen de sintaxis 📖

typeof

Devuelve una string con el nombre del tipo de un valor.

typeof 'Acme'

Array.isArray()

Devuelve true cuando el valor es un array. La forma confiable de verificarlo, ya que typeof no puede.

Array.isArray(['Small', 'Large'])

.length

La cantidad de ítems de un array, o la cantidad de caracteres de una string.

['a', 'b', 'c'].length
'Acme'.length

Acceder a valores

Corchetes y un número para los arrays, un punto y un nombre para los objetos.

sizes[0]
customer.name

La parte de pruebas 🧪

Los tipos no son algo académico. Aquí está el falso positivo más común de la automatización de pruebas web, y es un problema de tipos.

El texto leído de una página web siempre es un String, incluso cuando parece un número.

it('muestra el total correcto', () => {
  cy.get('[data-testid="total"]').invoke('text').then((total) => {
    // total es la string '42', no el número 42
    expect(Number(total)).to.equal(42)
  })
})

Sin esa conversión con Number(), comparar '42' con 42 de forma estricta falla, y compararlos de forma laxa pasa por razones que no pretendías. La lección 4 cubre exactamente esa diferencia.

El segundo lugar donde los tipos importan es en las pruebas de API, donde conocer el tipo esperado es la prueba.

it('devuelve un cliente bien formado', async ({ request }) => {
  const response = await request.get('/api/customers/1')
  const customer = await response.json()

  expect(typeof customer.name).toBe('string')
  expect(typeof customer.active).toBe('boolean')
  expect(Array.isArray(customer.contacts)).toBe(true)
})

A esa prueba no le importa cómo se llama el cliente. Le importa que name sea texto, que active sea un sí o no, y que contacts sea una lista. Si un cambio en el backend convierte active en la string 'true', todas las verificaciones de la interfaz podrían seguir pasando mientras esta lo detecta de inmediato.

👨‍🏫 Un campo que cambia de tipo es uno de los bugs más desagradables de encontrar después, porque el valor sigue pareciendo correcto cuando lo imprimes. 'true' y true se imprimen idénticos.

Contenido sugerido 📚

Ejercicio 🎯

Crea 01-data-types.js en tu fork y haz lo siguiente:

  1. Declara un valor de cada tipo primitivo: una string, un número, un booleano, un undefined y un null
  2. Declara un array con al menos tres strings, e imprime su longitud y su primer ítem
  3. Declara un objeto que describa a un cliente, con un nombre, un tamaño y si está activo, e imprime dos de sus campos
  4. Imprime el typeof de cada uno de tus cinco primitivos
  5. Imprime Array.isArray() tanto para tu array como para tu objeto

Ejecútalo con node 01-data-types.js, y después haz el commit y envíalo.

🙊 Aquí hay una forma de escribirlo:
const name = 'Acme Corporation'
const employees = 240
const active = true
let notSetYet
const phone = null

const sizes = ['Small', 'Medium', 'Large']
const customer = { name: 'Acme Corporation', size: 'Large', active: true }

console.log(sizes.length)
console.log(sizes[0])
console.log(customer.name, customer.active)

console.log(typeof name, typeof employees, typeof active, typeof notSetYet, typeof phone)
console.log(Array.isArray(sizes), Array.isArray(customer))
Dos cosas para notar en la salida.

typeof notSetYet imprime undefined aunque no se le asignó nada. Eso es JavaScript llenando el hueco, no un error.

typeof phone imprime object, no null. Ese es el error del lenguaje mencionado arriba. Cuando necesites verificar si algo es null, compáralo directamente en lugar de confiar en typeof.

Muéstrale al mundo lo que aprendiste 🌎

Para mostrarle a tu red profesional lo que aprendiste en esta lección, publica lo siguiente en LinkedIn.

Empecé el curso "JavaScript para QAs" de @Walmyr Lima e Silva Filho en la @Talking About Testing School, y la primera lección ya me explicó un falso positivo que había visto antes: el texto leído de una página siempre es una string, incluso cuando parece un número, así que compararlo con un número no es la verificación que creías haber escrito. #TalkingAboutTesting #TATSchool #JavaScriptForQAs #JavaScript #TestAutomation

👨‍🏫 No olvides mencionarme en tu publicación. Aquí está mi perfil de LinkedIn.

Cuestionario

Pregunta 1 de 2
Puntuación: 0

¿Qué devuelve `typeof [1, 2, 3]`, y qué deberías usar en su lugar?

Obtén 100% en el cuestionario para continuar