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:
-
Rojo. Escribimos una prueba que falla (incluyendo posibles fallos de compilación). Ejecutamos el conjunto de pruebas para verificar las pruebas que fallan.
-
Verde. Escribimos el código de producción suficiente para que la prueba sea verde. Ejecutamos el conjunto de pruebas para verificarlo.
-
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.
¿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
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
)
}
}
Declaración de envases
Paquete "pruebas" importado, utilizado posteriormente en
t.Errorf
Nuestro método de prueba, que debe empezar por
Test
y tener un argumento*testing.T
La estructura que representa "USD 5."
Dollar
todavía no existeMétodo sometido a prueba:
Times
-que tampoco existe todavía-.Comparar el valor real con el valor esperado
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 func
tion 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'
)
;
let
fiver
=
new
Dollar
(
5
)
;
let
tenner
=
fiver
.
times
(
2
)
;
assert
.
strictEqual
(
tenner
.
amount
,
10
)
;
Importar el paquete
assert
, necesario para la afirmación posteriorEl objeto que representa "USD 5."
Dollar
todavía no existeMétodo sometido a prueba:
times
-que tampoco existe todavía-.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
class
TestMoney
(
unittest
.
TestCase
)
:
def
testMultiplication
(
self
)
:
fiver
=
Dollar
(
5
)
tenner
=
fiver
.
times
(
2
)
self
.
assertEqual
(
10
,
tenner
.
amount
)
if
__name__
==
'
__main__
'
:
unittest
.
main
(
)
Importando el paquete
unittest
, se necesita para la superclaseTestCase
.Nuestra clase de prueba, que debe subclasificar a la clase
unittest.TestCase
.El nombre de nuestro método debe empezar por
test
para ser considerado un método de prueba.El objeto que representa "USD 5."
Dollar
todavía no existe.Método sometido a prueba:
times
-que tampoco existe todavía-.Comparar el valor real con el valor esperado en una declaración
assertEqual
.El lenguaje
main
garantiza que esta clase pueda ejecutarse como un script.
Python requiere import
ing el paquete unittest
, crear una clase que subclase TestCase
, y def
ining 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!
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:
-
Acabamos de terminar el primer paso -llegar al rojo- denuestra primera prueba. No sólo es el principio, sino el principio del principio.
-
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 func
tió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.00
s
)
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'
)
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
)
{
this
.
amount
=
amount
;
}
times
(
multiplier
)
{
return
new
Dollar
(
10
)
;
}
}
La función
constructor
se llama cada vez que se crea un objetoDollar
.Inicializa la variable
this.amount
con el parámetro dado.El método
times
recibe un parámetro.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
)
:
self
.
amount
=
amount
def
times
(
self
,
multiplier
)
:
return
Dollar
(
10
)
La función
__init__
se llama cada vez que se crea un objetoDollar
.Inicializa la variable
self.amount
con el parámetro dado.El método
times
recibe un parámetro.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.000
s
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:
-
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. -
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 escribimos10
tanto en nuestra prueba como en nuestroDollar
código. Deberíamos darnos cuenta de que el10
de la entidadDollar
es en realidad5 * 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
}
}
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
.
git
commit
-m
"feat: first green test"
Añade todos los archivos, incluidos todos los cambios realizados en ellos, al índice Git.
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
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
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
)
Author:
Saleem
Siddiqui
...
Date:
Sun
Mar
7
12:26:06
2021
-0600
feat:
first
green
test
Este es el primer commit, con su hash SHA completo.
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.