Capítulo 1. El problema del dinero

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

No daría una higa por la simplicidad a este lado de la complejidad, pero daría mi vida por la simplicidad al otro lado de la complejidad.

Oliver Wendell Holmes Jr.

Nuestro entorno de desarrollo está listo. En este capítulo, aprenderemos las tres fases que sustentan el desarrollo dirigido por pruebas. A continuación, escribiremos nuestra primera función de código utilizando el desarrollo dirigido por pruebas.

Rojo-Verde-Refactor: Los pilares del TDD

El desarrollo basado en pruebas sigue un proceso de tres fases:

  1. Rojo. Escribimos una prueba que falla (incluyendo posibles fallos de compilación). Ejecutamos el conjunto de pruebas para verificar las pruebas que fallan.

  2. Verde. Escribimos el código de producción suficiente para que la prueba sea verde. Ejecutamos el conjunto de pruebas para verificarlo.

  3. Refactorización. Eliminamos cualquier código que huela mal. Pueden deberse a duplicación, valores codificados o uso inadecuado de expresiones idiomáticas del lenguaje (por ejemplo, utilizar un bucle verboso en lugar de un iterador incorporado). Si rompemos alguna prueba durante la refactorización, damos prioridad a que vuelva a estar en verde antes de salir de esta fase.

Se trata del ciclo rojo-verde-refactor (RGR), que se muestra en la Figura 1-1. Las tres fases de este ciclo son los componentes esenciales del desarrollo basado en pruebas. Todo el código que desarrollaremos en este libro seguirá este ciclo.

The Red Green Refactor (RGR) cycle. Red: write a failing test. Green: write just enough production code to pass the test. Refactor: remove code smells, e.g. duplication or poor design, without adding new features.
Figura 1-1. El ciclo rojo-verde-refactor es la base sobre la que descansa el desarrollo basado en pruebas
Importante

Las tres fases del ciclo rojo-verde-refactor son los componentes esenciales del TDD.

¿Cuál es el problema?

Tenemos un problema de dinero. No, no del tipo que tiene casi todo el mundo: ¡no tener suficiente! Es más bien un problema de "queremos controlar nuestro dinero".

Supongamos que tenemos que construir una hoja de cálculo para gestionar dinero en más de una moneda, quizá para gestionar una cartera de acciones.

Acciones Bolsa Acciones Precio de las acciones Total

IBM

NASDAQ

100

124 USD

12400 USD

BMW

DAX

400

75 EUR

30000 EUR

Samsung

KSE

300

68000 KRW

20400000 KRW

Para construir esta hoja de cálculo, necesitaríamos hacer operaciones aritméticas sencillas con números de cualquier moneda:

5 USD × 2 = 10 USD

10 EUR × 2 = 20 EUR

4002 KRW / 4 = 1000,5 KRW

También nos gustaría convertir entre divisas. Por ejemplo, si al cambiar 1 EUR obtenemos 1,2 USD, y al cambiar 1 USD obtenemos 1100 KRW:

5 USD + 10 EUR = 17 USD

1 USD + 1100 KRW = 2200 KRW

Cada una de las líneas mencionadas será una (pequeñísima) característica que implementaremos utilizando TDD. Ya tenemos varias funciones que implementar. Para ayudarnos a centrarnos en una cosa cada vez, resaltaremos en negrita la función en la que estamos trabajando. Cuando hayamos terminado con una función, señalaremos nuestra satisfaccióntachándola.

¿Por dónde empezamos? Por si el título de este libro no te delata, empezaremos escribiendo una prueba.

Nuestro primer examen fallido

Empecemos implementando la primera función de de nuestra lista:

5 USD × 2 = 10 USD

10 EUR × 2 = 20 EUR

4002 KRW / 4 = 1000,5 KRW

5 USD + 10 EUR = 17 USD

1 USD + 1100 KRW = 2200 KRW

Empezaremos escribiendo una prueba fallida, correspondiente a la fase roja del ciclo RGR.

Ve a

En un nuevo archivo llamado money_test.go en la carpeta go, vamos a escribir nuestra primera prueba:

package main 1

import (
    "testing" 2
)

func TestMultiplication(t *testing.T) { 3
    fiver := Dollar{ 4
        amount: 5,
    }
    tenner := fiver.Times(2) 5
    if tenner.amount != 10 { 6
        t.Errorf("Expected 10, got: [%d]", tenner.amount) 7
    }
}
1

Declaración de envases

2

Paquete "pruebas" importado, utilizado posteriormente en t.Errorf

3

Nuestro método de prueba, que debe empezar por Test y tener un argumento *testing.T

4

La estructura que representa "USD 5." Dollar todavía no existe

5

Método sometido a prueba: Times-que tampoco existe todavía-.

6

Comparar el valor real con el valor esperado

7

Asegurarse de que la prueba falla si el valor esperado no es igual al valor real

Esta función de prueba incluye un poco de código repetitivo.

El package main declara que todo el código subsiguiente forma parte del paquete main. Éste es un requisito para los programas Go independientes y ejecutables. La gestión de paquetes es una característica sofisticada de Go. Se trata con más detalle en el Capítulo 5.

A continuación, importamos el paquete testing utilizando la sentencia import. Este paquete se utilizará en la prueba unitaria.

La prueba unitaria function es la mayor parte del código. Declaramos una entidad que representa "5 USD". Se trata de la variable llamada fiver, que inicializamos a una estructura que contiene 5 en su campo amount. A continuación, multiplicamos fiver por 2. Y esperamos que el resultado sea 10 dólares, es decir, una variable tenner cuyo campo amount debe ser igual a 10. Si no es así, imprimimos un mensaje de error bien formateado con el valor real (sea cual sea).

Cuando ejecutamos esta prueba utilizando "go test -v ." desde la carpeta raíz del proyecto TDD, deberíamos obtener un error:

... undefined: Dollar
FAIL	tdd [build failed]
FAIL

Recibimos el mensaje alto y claro: ¡es nuestra primera prueba fallida!

Consejo

"go test -v ." ejecuta las pruebas en la carpeta actual, y "go test -v ./..."1 ejecuta las pruebas en la carpeta actual y en las subcarpetas. El modificador -v produce una salida detallada.

JavaScript

En un nuevo archivo llamado test_money.js en la carpeta js, vamos a escribir nuestra primera prueba:

const assert = require('assert'); 1

let fiver = new Dollar(5); 2
let tenner = fiver.times(2); 3
assert.strictEqual(tenner.amount, 10); 4
1

Importar el paquete assert, necesario para la afirmación posterior

2

El objeto que representa "USD 5." Dollar todavía no existe

3

Método sometido a prueba: times-que tampoco existe todavía-.

4

Comparación del valor real con el valor esperado en una sentencia assert strictEqual

JavaScript tiene un código boilerplate mínimo: la única línea además del código de prueba es la declaración require. Esto nos da acceso al paquete NPM assert.

Después de esa línea están las tres líneas de código que forman nuestra prueba. Creamos un objeto que representa 5 USD, lo multiplicamos por 2 y esperamos que el resultado sea 10.

Importante

ES2015 introdujo la palabra clave let para declarar variables y la palabra clave const para declarar constantes.

Cuando ejecutamos este código desde la carpeta raíz del proyecto TDD utilizando node js/test_money.js, deberíamos obtener un error que empieza así:

ReferenceError: Dollar is not defined

Es nuestra primera prueba fallida. ¡Hurra!

Consejo

node file.js ejecuta el código JavaScript en file.js y produce un resultado. Utilizamos este comando para ejecutar nuestras pruebas.

Python

En un nuevo archivo llamado test_money.py en la carpeta py, vamos a escribir nuestra primera prueba:

import unittest 1

class TestMoney(unittest.TestCase): 2
  def testMultiplication(self): 3
    fiver = Dollar(5) 4
    tenner = fiver.times(2) 5
    self.assertEqual(10, tenner.amount) 6

if __name__ == '__main__': 7
    unittest.main()
1

Importando el paquete unittest, se necesita para la superclase TestCase.

2

Nuestra clase de prueba, que debe subclasificar a la clase unittest.TestCase.

3

El nombre de nuestro método debe empezar por test para ser considerado un método de prueba.

4

El objeto que representa "USD 5." Dollar todavía no existe.

5

Método sometido a prueba: times-que tampoco existe todavía-.

6

Comparar el valor real con el valor esperado en una declaración assertEqual.

7

El lenguaje main garantiza que esta clase pueda ejecutarse como un script.

Python requiere importing el paquete unittest, crear una clase que subclase TestCase, y defining una función cuyo nombre empiece por test. Para poder ejecutar la clase como un programa independiente, necesitamos el lenguaje común de Python que ejecuta la función unittest.main() cuando se ejecuta directamente test_money.py.

La función de prueba describe cómo esperamos que funcione nuestro código. Definimos una variable llamada fiver y la inicializamos en una clase deseada (pero aún por crear) Dollar con 5 como argumento constructor. A continuación, multiplicamos fiver por 2 y almacenamos el resultado en una variable tenner. Por último, esperamos que el amount de tenner sea 10.

Cuando ejecutamos este código desde la carpeta TDD_PROJECT_ROOT utilizando python3 py/test_money.py -v, obtenemos un error:

NameError: name 'Dollar' is not defined

Es nuestra primera prueba fallida. ¡Hurra!

Consejo

python3 file.py -v ejecuta el código Python en file.py y produce una salida detallada. Utilizamos este comando para ejecutar nuestras pruebas.

Apuesta por lo verde

Escribimos nuestras pruebas como esperaríamos que funcionaran, ignorando alegremente todos los errores de sintaxis por el momento. ¿Es esto inteligente?

Al principio -que es donde nos encontramos- es inteligente empezar con el mínimo trozo de código que nos ponga en el camino del progreso. Por supuesto, nuestras pruebas fallan porque no hemos definido qué es Dollar. Este puede parecer el momento perfecto para decir "¡Duh!". Sin embargo, un poco de paciencia está justificada por estas dos razones:

  1. Acabamos de terminar el primer paso -llegar al rojo- denuestra primera prueba. No sólo es el principio, sino el principio del principio.

  2. Podemos (y lo haremos) acelerar los incrementos a medida que avanzamos. Sin embargo, es importante saber que podemos ir más despacio cuando lo necesitemos.

La siguiente fase del ciclo RGR es llegar al verde.

Está claro que necesitamos introducir una abstracción Dollar. Esta sección define cómo introducir ésta y otras abstracciones para que nuestra prueba pase.

Ve a

Añade un Dollar struct vacío a al final de money_test.go.

type Dollar struct {
}

Cuando ejecutamos ahora la prueba, obtenemos un nuevo error:

... unknown field 'amount' in struct literal of type Dollar

¡Progreso!

El mensaje de error nos indica que introduzcamos un campo llamado amount en nuestra estructura Dollar. Así que vamos a hacerlo, utilizando por ahora un tipo de datos int (que es suficiente para nuestro objetivo):

type Dollar struct {
    amount int
}

Si añadimos Dollar struct, como era de esperar, llegamos al siguiente error:

... fiver.Times undefined (type Dollar has no field or method Times)

Aquí vemos un patrón: cuando hay algo (un campo o un método) que está indefinido, obtenemos este error undefined del tiempo de ejecución Go. Utilizaremos esta información para acelerar nuestros ciclos de TDD en el futuro. Por ahora, vamos a añadir una functión llamada Times. Sabemos, por cómo escribimos nuestra prueba, que esta función debe tomar un número (el multiplicador) y devolver otro número (el resultado).

Pero, ¿cómo debemos calcular el resultado? Conocemos la aritmética básica: cómo multiplicar dos números. Pero si escribiéramos el código más sencillo que funcione, estaría justificado que devolviéramos siempre el resultado que espera nuestra prueba, es decir, una estructura que representa 10 dólares:

func (d Dollar) Times(multiplier int) Dollar {
    return Dollar{10}
}

Cuando ejecutemos ahora nuestro código, deberíamos obtener una respuesta breve y dulce en nuestro terminal:

=== RUN   TestMultiplication
--- PASS: TestMultiplication (0.00s)
PASS

Ésa es la palabra mágica: ¡hicimos nuestra prueba PASS!

JavaScript

En test_money.js, justo después de la línea const assert = require('assert');, define una clase vacía llamada Dollar:

class Dollar {
}

Cuando ahora ejecutamos el archivo test_money.js, obtenemos un error:

TypeError: fiver.times is not a function

¡Progresa! El error indica claramente que no hay ninguna función llamada times definida para el objeto llamado fiver. Así que vamos a introducirla dentro de la clase Dollar:

class Dollar {
    times(multiplier) {
    }
}

Ejecutar la prueba produce ahora un nuevo error:

TypeError: Cannot read properties of undefined (reading 'amount') 1
1

Este mensaje es de Node.js v16; v14 produce un mensaje de error ligeramente diferente

Nuestra prueba espera un objeto con una propiedad amount. Como no devolvemos nada de nuestro método times, el valor devuelto es undefined, que no tiene una propiedad amount (ni ninguna otra propiedad, por cierto).

Consejo

En el lenguaje JavaScript, las funciones y los métodos no declaran explícitamente ningún tipo de retorno. Si examinamos el resultado de una función que no devuelve nada, veremos que el valor de retorno es undefined.

Entonces, ¿cómo debemos hacer que nuestra prueba sea ecológica? ¿Qué es lo más sencillo que podría funcionar? ¿Qué tal si creamos siempre un objeto que represente 10 USD y lo devolvemos?

Vamos a probarlo. Añadimos un constructor que inicializa los objetos con una cantidad determinada y un método times que crea y devuelve obstinadamente objetos "10 USD":

class Dollar {
    constructor(amount) { 1
        this.amount = amount; 2
    }

    times(multiplier) { 3
        return new Dollar(10); 4
    }
}
1

La función constructor se llama cada vez que se crea un objeto Dollar.

2

Inicializa la variable this.amount con el parámetro dado.

3

El método times recibe un parámetro.

4

Implementación sencilla: devuelve siempre 10 dólares.

Cuando ejecutemos ahora nuestro código, no deberíamos obtener ningún error. ¡Esta es nuestra primera prueba en verde!

Importante

Como strictEqual y otros métodos del paquete assert sólo producen salida cuando fallan las aserciones, una prueba ejecutada con éxito será bastante silenciosa, sin salida. Mejoraremos este comportamiento en el Capítulo 6.

Python

Ya que 'Dollar' is not defined, vamos a definir en test_money.py antes de nuestra clase TestMoney:

class Dollar:
  pass

Cuando ejecutamos ahora nuestro código, obtenemos un error:

TypeError: Dollar() takes no arguments

¡Progreso! El error nos está diciendo claramente que actualmente no hay forma de inicializar Dollar con ningún argumento, como los 5 y 10 que tenemos en nuestro código. Así que vamos a solucionarlo proporcionando el inicializador más breve posible:

class Dollar:
  def __init__(self, amount):
    pass

Ahora cambia el mensaje de error de nuestra prueba:

AttributeError: 'Dollar' object has no attribute 'times'

Aquí vemos un patrón: nuestra prueba sigue fallando, pero por razones ligeramente distintas cada vez. A medida que definimos nuestras abstracciones -primero Dollar y luego un campo amount - los mensajes de error "mejoran" hasta la siguiente etapa. Este es un rasgo distintivo de TDD: un progreso constante a un ritmo que controlamos.

Aceleremos un poco las cosas definiendo una función times y dándole el comportamiento mínimo para llegar al verde. ¿Cuál es el comportamiento mínimo necesario? Devolver siempre el objeto "diez dólares" que requiere nuestra prueba, ¡por supuesto!

class Dollar:
  def __init__(self, amount): 1
    self.amount = amount 2

  def times(self, multiplier): 3
    return Dollar(10) 4
1

La función __init__ se llama cada vez que se crea un objeto Dollar.

2

Inicializa la variable self.amount con el parámetro dado.

3

El método times recibe un parámetro.

4

Una aplicación sencilla consiste en devolver siempre 10 dólares.

Cuando ejecutamos ahora nuestra prueba, obtenemos una respuesta breve y dulce:

Ran 1 test in 0.000s

OK

Es posible que la prueba no se ejecute en 0.000s, pero no perdamos de vista la palabra mágica OK. ¡Ésta es nuestra primera prueba verde!

Limpieza

¿Te sientes desconcertado porque hayamos llegado al verde codificando "10 USD" en nuestras pruebas? No te preocupes: la etapa de refactorización nos permite abordar esta incomodidad descubriendo cómo podemos eliminar el valor "10 USD" codificado y duplicado.

Refactorizar es la tercera y última etapa del ciclo RGR. Puede que no tengamos muchas líneas de código en este punto; sin embargo, sigue siendo importante mantener las cosas ordenadas y compactas. Si tenemos algún desorden de formato o líneas de código comentadas, ahora es el momento de limpiarlo.

Más importante es la necesidad de eliminar la duplicación y hacer que el código sea legible. A primera vista, puede parecer que en las aproximadamente 20 líneas de código que hemos escrito no puede haber ninguna duplicación. Sin embargo, ya existe una sutil pero significativa duplicación.

Podemos encontrar esta duplicación observando un par de peculiaridades en nuestro código:

  1. Hemos escrito el código suficiente para verificar que "doblar 5 dólares debería darnos 10 dólares". Si decidimos cambiar nuestra prueba existente para que diga "duplicar 10 dólares debería darnos 20 dólares" -una afirmación igualmente sensata-, tendremos que cambiar tanto nuestra prueba como nuestro código Dollar. Existe una dependencia, un acoplamiento lógico, entre los dos segmentos de código. En general, este tipo de acoplamiento debe evitarse.

  2. Tanto en nuestra prueba como en nuestro código, teníamos el número mágico 10. ¿De dónde lo hemos sacado? Obviamente, hicimos las cuentas mentalmente. Nos dimos cuenta de que doblando 5 dólares deberíamos obtener 10 dólares. Así que escribimos 10 tanto en nuestra prueba como en nuestro Dollar código. Deberíamos darnos cuenta de que el 10 de la entidad Dollar es en realidad 5 * 2. Esta comprensión nos permitiría eliminar esta duplicación.

El código duplicado suele ser el síntoma de algún problema subyacente: una abstracción de código ausente o un mal acoplamiento entre las distintas partes del código de.2

Eliminemos la duplicación y así nos libraremos también del acoplamiento.

Ve a

Sustituye el 10 de la función Times por su equivalente 5 * 2:

func (d Dollar) Times(multiplier int) Dollar {
    return Dollar{5 * 2}
}

La prueba debe seguir siendo verde.

Escribiendo de esta forma nos damos cuenta de la abstracción que falta. El 5 codificado es en realidad d.amount, y el 2 es el multiplier. Sustituyendo estos números codificados por las variables correctas obtenemos la implementación no trivial:

func (d Dollar) Times(multiplier int) Dollar {
    return Dollar{d.amount * multiplier}
}

¡Sí! La prueba sigue pasando, y hemos eliminado la duplicación y el acoplamiento.

Hay una última limpieza.

En nuestra prueba, hemos utilizado explícitamente el nombre de campo amount al inicializar una estructura Dollar. También es posible omitir los nombres de campo al inicializar una estructura, como hicimos en nuestro método Times.3 Cualquiera de los dos estilos -utilizar nombres explícitos o no utilizarlos- funciona. Sin embargo, es importante ser coherente. Cambiemos la función Times para especificar el nombre del campo:

func (d Dollar) Times(multiplier int) Dollar {
    return Dollar{amount: d.amount * multiplier}
}
Consejo

Recuerda ejecutar go fmt ./... periódicamente para corregir cualquier problema de formato en el código.

JavaScript

Sustituyamos el 10 del método times por su equivalente 5 * 2:

    times(multiplier) {
        return new Dollar(5 * 2);
    }

La prueba debe seguir siendo verde.

Ahora está clara la abstracción que falta. Podemos sustituir 5 por this.amount y 2 por multiplier:

    times(multiplier) {
        return new Dollar(this.amount * multiplier);
    }

¡Sí! La prueba sigue en verde y hemos eliminado tanto el 10 duplicado como el acoplamiento.

Python

Sustituyamos el 10 del método times por su equivalente 5 * 2:

  def times(self, multiplier):
    return Dollar(5 * 2)

La prueba se mantiene en verde, como era de esperar.

Esto revela la abstracción subyacente. El 5 es realmente self.amount y el 2 es el multiplier:

  def times(self, multiplier):
    return Dollar(self.amount * multiplier)

¡Hurra! La prueba sigue en verde, y la duplicación y el acoplamiento han desaparecido.

Comprometer nuestros cambios

Hemos terminado nuestra primera funcionalidad utilizando TDD. Para que no se nos olvide, es importante confirmar nuestro código en nuestro repositorio de control de versiones a intervalos frecuentes.

Una prueba verde es un lugar excelente para comprometer código.

En una ventana shell, escribamos estos dos comandos:

git add . 1
git commit -m "feat: first green test" 2
1

Añade todos los archivos, incluidos todos los cambios realizados en ellos, al índice Git.

2

Consigna el índice Git en el repositorio con el mensaje dado.

Suponiendo que exista código para los tres idiomas en las carpetas correctas, deberíamos obtener un mensaje como éste.

[main (root-commit) bb31b94] feat: first green test 1
 4 files changed, 56 insertions(+)
 create mode 100644 go/go.mod
 create mode 100644 go/money_test.go
 create mode 100644 js/test_money.js
 create mode 100644 py/test_money.py
1

El número hexadecimal, bb31b94, representa los primeros dígitos del "hash SHA" único asociado al commit. Será diferente para cada persona (y cada confirmación).

Esto indica que todos nuestros archivos están a salvo en nuestro repositorio de control de versiones Git. Podemos comprobarlo ejecutando el comando git log en nuestro intérprete de comandos, que debería producir una salida similar a la siguiente:

commit bb31b94e90029ddeeee89f3ca0fe099ea7556603 (HEAD -> main) 1
Author: Saleem Siddiqui ...
Date:   Sun Mar 7 12:26:06 2021 -0600

    feat: first green test 2
1

Este es el primer commit, con su hash SHA completo.

2

Este es el mensaje que escribimos para nuestro primer commit.

Es importante darse cuenta de que el repositorio Git en el que hemos comprometido nuestro código también reside en nuestro sistema de archivos local. (Está dentro de la carpeta .git bajo nuestro TDD_PROJECT_ROOT). Aunque esto no nos salva de derrames accidentales de café en nuestro ordenador (usa siempre tapas), sí nos da la seguridad de que podemos volver a una versión buena conocida anterior si nos enredamos en algún sitio. En el Capítulo 13, subiremos todo nuestro código a un repositorio de GitHub.

Utilizaremos esta estrategia de enviar nuestro código a nuestro repositorio Git local en cada capítulo, utilizando el mismo conjunto de comandos.

Importante

Utilizaremos los dos comandos git add . y git commit -m _commit message_ para confirmar frecuentemente nuestro código en cada capítulo.

Lo único que variará será el mensaje de confirmación, que seguirá el estilo de confirmación semántica e incluirá una breve descripción de una línea de los cambios.

Consejo

Los mensajes git commit de este libro siguen el estilo de confirmación semántica.

Dónde estamos

Este capítulo ha introducido el desarrollo basado en pruebas mostrando el primer ciclo rojo-verde-refactor. Con nuestra primera pequeña característica implementada con éxito, vamos a tacharla. Aquí es donde estamos en nuestra lista de características:

5 USD × 2 = 10 USD

10 EUR × 2 = 20 EUR

4002 KRW / 4 = 1000,5 KRW

5 USD + 10 EUR = 17 USD

1 USD + 1100 KRW = 2200 KRW

Tomémonos un momento para revisar y saborear nuestro código antes de pasar al siguiente reto. A continuación reproducimos el código fuente de los tres lenguajes. También está accesible en el repositorio de GitHub. En aras de la brevedad, en los próximos capítulos sólo se indicará el nombre de la rama correspondiente.

Ve a

Este es el aspecto actual del archivo money_test.go :

package main

import (
    "testing"
)

func TestMultiplication(t *testing.T) {
    fiver := Dollar{amount: 5}
    tenner := fiver.Times(2)
    if tenner.amount != 10 {
        t.Errorf("Expected 10, got: [%d]", tenner.amount)
    }
}

type Dollar struct {
    amount int
}

func (d Dollar) Times(multiplier int) Dollar {
    return Dollar{amount: d.amount * multiplier}
}

JavaScript

Este es el aspecto del archivo test_money.js en este punto:

const assert = require('assert');

class Dollar {
    constructor(amount) {
      this.amount = amount;
    }

    times(multiplier) {
        return new Dollar(this.amount * multiplier);
    }
}

let fiver = new Dollar(5);
let tenner = fiver.times(2);
assert.strictEqual(tenner.amount, 10);

Python

Este es el aspecto actual del archivo test_money.py :

import unittest

class Dollar:
  def __init__(self, amount):
    self.amount = amount

  def times(self, multiplier):
    return Dollar(self.amount * multiplier)

class TestMoney(unittest.TestCase):
  def testMultiplication(self):
    fiver = Dollar(5)
    tenner = fiver.times(2)
    self.assertEqual(10, tenner.amount)

if __name__ == '__main__':
    unittest.main()
Consejo

El código de este capítulo está en una rama llamada "chap01" en el repositorio de GitHub. Hay una rama para cada capítulo en la que se desarrolla el código.

En el Capítulo 2, aceleraremos las cosas creando un par de funciones más.

1 Los tres puntos de "go test -v ./..." y "go fmt ./..." deben escribirse literalmente; ¡son los únicos casos de este libro en los que no significan código omitido!

2 Merece la pena citar aquí la opinión de Kent Beck: "Si la dependencia es el problema, la duplicación es el síntoma".

3 Si hay varios campos en la estructura -que actualmente no los hay-, o bien el orden de los campos debe ser el mismo tanto en la definición como en la inicialización de la estructura, o bien deben especificarse los nombres de los campos durante la inicialización de la estructura. Consulta https://gobyexample.com/structs.

Get Aprender el desarrollo basado en pruebas now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.