-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
059f8cd
commit ba0de01
Showing
3 changed files
with
202 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 97 additions & 0 deletions
97
data/blog/en/katas-sustainable-testing-ts/kata-08-fibonacci.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
--- | ||
title: "Kata 08 in TypeScript: Fibonacci" | ||
series: | ||
order: 9 | ||
title: "Sustainable Testing Katas in TypeScript" | ||
date: '2024/10/19' | ||
lastmod: '2024/10/19' | ||
language: en | ||
tags: ['testing', 'tdd', 'javascript', 'typescript'] | ||
authors: ['default'] | ||
draft: false | ||
featured: false | ||
summary: En este artículo de la serie "Sustainable Testing Katas in TypeScript", analizaremos la creación de una función que calcula el enésimo número de Fibonacci usando TDD y Jest. | ||
--- | ||
|
||
> The katas of this series are proposed exercises in the excellent course [Testing Sostenible con TypeScript](https://academy.softwarecrafters.io/p/curso) by Miguel A. Gómez and Carlos Blé | ||
## Introduction | ||
|
||
The [Fibonacci sequence](https://www.cuemath.com/numbers/fibonacci-sequence/) is a fascinating mathematical sequence, with applications that range from biology to informatics. In this article, we will explore how to apply TDD to implement a function that calculates the nth [Fibonacci](https://www.thoughtco.com/leonardo-pisano-fibonacci-biography-2312397) number. We will see how the development patterns known as [Green Bar Patterns](https://www.tddbuddy.com/references/green-bar-patterns.html), proposed by [Kent Beck](https://londonspeakerbureau.com/speaker-profile/kent-beck/), guide us to an effective solution. | ||
|
||
<TOCInline toc={props.toc} exclude="Introduction" locale={props.locale} asDisclosure /> | ||
|
||
## Fibonacci Sequence | ||
|
||
The Fibonacci sequence starts with the numbers 0 and 1, and each subsequent number is the sum of the previous two: 0, 1, 1, 2, 3, 5, 8, and so on. This sequence, popularized in Europe by the Italian mathematician Fibonacci, is a classic example of exponential growth and it is found in various areas of study. | ||
|
||
Our goal is to build a `fibonacci` function which, given an integer number `n`, returns the nth Fibonacci number. We use TDD not only to ensure that our implementation is correct, but also to illustrate the `Green Bar Patterns`. | ||
|
||
## Green Bar Patterns | ||
|
||
To keep the article brief, we will see the implementations and the tests will be understood from the examples. | ||
|
||
### Fake Implementation | ||
|
||
We start with a fake implementation to make a test pass quickly. For example, for `fibonacci(0)`, we simply return 0. | ||
|
||
```ts | ||
function fibonacci(number: number) { | ||
return 0; | ||
} | ||
``` | ||
|
||
### Triangulation | ||
|
||
By adding more test cases, we use triangulation to refine our implementation. This pattern helps us to generalize the solution from specific cases. For example, we handle `fibonacci(1)` by returning 1 and then we verify that `fibonacci(2)` is the sum of the previous two. | ||
|
||
```ts | ||
function fibonacci(number: number) { | ||
if (number === 0) return 0; | ||
return 1; | ||
} | ||
``` | ||
|
||
This would work for `fibonacci(2)` because: | ||
|
||
`fibonacci(2)` $=$ `1` | ||
|
||
`fibonacci(2)` $=$ `fibonacci(1)` $+$ `fibonacci(0)` $=$ `1` $+$ `0` $=$ `1` | ||
|
||
### Obvious Implementation | ||
|
||
As we add more cases, we move on to a recursive implementation that reflects the mathematical definition of Fibonacci. This allows us to handle any number in the sequence. | ||
|
||
```ts | ||
export function fibonacci(number: number) { | ||
if (number === 0) return 0; | ||
if (number === 1) return 1; | ||
return fibonacci(number - 1) + fibonacci(number - 2); | ||
} | ||
``` | ||
|
||
It may seem odd that the obvious implementation is not necessarily of one line or very short, but in this case, by implementing a function that follows a mathematical definition, it is possible to say that it is trivial to translate this definition into a function. | ||
|
||
It is also important to mention that if we know the obvious implementation, we have to use it and there is no need for us to go through fake implementation or triangulation. | ||
|
||
### One to Many | ||
|
||
This pattern is the last one. It doesn't apply here, since it is used when we have data collections and we start from the case of a single element and at the end we generalize for many. But it is good for us to know it because by understanding it, it is obvious that the solution to a problem of this type is easier to reach if we take a specific case to build the general case. This is what is known in logic as **induction**. | ||
|
||
## Challenges Encountered | ||
|
||
- **Recursion Efficiency.** Recursion is clear and direct, but it can be inefficient for big numbers because of redundant calculations. Considering memoization or an iterative implementation is key to improve performance. | ||
|
||
- **Exhaustive Test Coverage.** Ensuring that all variations, from the base cases to higher numbers, are covered by tests is crucial for code robustness. | ||
|
||
- **Continuous Refactoring.** Keeping the code clean and modular is essential. It is common that we initially place implementations and tests in the same file, so this process involves moving the `fibonacci` function out of the file containing the tests, in addition to optimizing the code structure. | ||
|
||
## Link to GitHub repository | ||
|
||
You can find this kata, and the rest of them, [here](https://github.com/carlos-talavera/katas-sustainable-testing-ts). | ||
|
||
## Conclusion | ||
|
||
This exercise of implementing the Fibonacci sequence using TDD provides us a valuable opportunity to practice development patterns and face common programming challenges. Through the fake implementation, triangulation, the obvious implementation and the one-to-many approach, we not only achieve the right solution, but also refine our ability to think in a systematic and disciplined way. That's why by applying these patterns and practices, we can address complex problems with confidence and clarity. | ||
|
||
I hope this article has helped you to understand these patterns. If you have a question or want to share something, leave it in the comments :) |
97 changes: 97 additions & 0 deletions
97
data/blog/es/katas-sustainable-testing-ts/kata-08-fibonacci.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
--- | ||
title: "Kata 08 con TypeScript: Fibonacci" | ||
series: | ||
order: 9 | ||
title: "Katas Testing Sostenible con TypeScript" | ||
date: '2024/10/19' | ||
lastmod: '2024/10/19' | ||
language: es | ||
tags: ['testing', 'tdd', 'javascript', 'typescript'] | ||
authors: ['default'] | ||
draft: false | ||
featured: false | ||
summary: En este artículo de la serie "Katas Testing Sostenible con TypeScript", analizaremos la creación de una función que calcula el enésimo número de Fibonacci usando TDD y Jest. | ||
--- | ||
|
||
> Las katas de esta serie son ejercicios propuestos en el excelente curso [Testing Sostenible con TypeScript](https://academy.softwarecrafters.io/p/curso) de Miguel A. Gómez y Carlos Blé | ||
## Introducción | ||
|
||
La [serie de Fibonacci](https://www.cuemath.com/numbers/fibonacci-sequence/) es una sucesión matemática fascinante, con aplicaciones que abarcan desde la biología hasta la informática. En este artículo, exploraremos cómo aplicar TDD para implementar una función que calcula el enésimo número de [Fibonacci](https://www.thoughtco.com/leonardo-pisano-fibonacci-biography-2312397). Veremos cómo los patrones de desarrollo conocidos como [Green Bar Patterns](https://www.tddbuddy.com/references/green-bar-patterns.html), propuestos por [Kent Beck](https://londonspeakerbureau.com/speaker-profile/kent-beck/), nos guían hacia una solución efectiva. | ||
|
||
<TOCInline toc={props.toc} exclude="Introducción" locale={props.locale} asDisclosure /> | ||
|
||
## Serie de Fibonacci | ||
|
||
La serie de Fibonacci comienza con los números 0 y 1, y cada número subsiguiente es la suma de los dos anteriores: 0, 1, 1, 2, 3, 5, 8, y así sucesivamente. Esta secuencia, popularizada en Europa por el matemático italiano Fibonacci, es un ejemplo clásico de crecimiento exponencial y se encuentra en diversas áreas de estudio. | ||
|
||
Nuestro objetivo es construir una función `fibonacci` que, dado un número entero `n`, devuelva el enésimo número de Fibonacci. Utilizamos TDD no solo para asegurar que nuestra implementación es correcta, sino también para ilustrar los `Green Bar Patterns`. | ||
|
||
## Green Bar Patterns | ||
|
||
Para mantener el artículo breve, veremos las implementaciones y las pruebas se sobreentenderán a partir de los ejemplos. | ||
|
||
### Implementación Fake | ||
|
||
Comenzamos con una implementación falsa para hacer que una prueba pase rápidamente. Por ejemplo, para `fibonacci(0)`, simplemente devolvemos 0. | ||
|
||
```ts | ||
function fibonacci(number: number) { | ||
return 0; | ||
} | ||
``` | ||
|
||
### Triangulación | ||
|
||
Al agregar más casos de prueba, utilizamos la triangulación para refinar nuestra implementación. Este patrón nos ayuda a generalizar la solución a partir de casos específicos. Por ejemplo, manejamos `fibonacci(1)` devolviendo 1 y luego verificamos que `fibonacci(2)` sea la suma de los dos anteriores. | ||
|
||
```ts | ||
function fibonacci(number: number) { | ||
if (number === 0) return 0; | ||
return 1; | ||
} | ||
``` | ||
|
||
Esto funcionaría para `fibonacci(2)` porque: | ||
|
||
`fibonacci(2)` $=$ `1` | ||
|
||
`fibonacci(2)` $=$ `fibonacci(1)` $+$ `fibonacci(0)` $=$ `1` $+$ `0` $=$ `1` | ||
|
||
### Implementación Obvia | ||
|
||
A medida que añadimos más casos, pasamos a una implementación recursiva que refleja la definición matemática de Fibonacci. Esto nos permite manejar cualquier número en la secuencia. | ||
|
||
```ts | ||
export function fibonacci(number: number) { | ||
if (number === 0) return 0; | ||
if (number === 1) return 1; | ||
return fibonacci(number - 1) + fibonacci(number - 2); | ||
} | ||
``` | ||
|
||
Puede parecer extraño que la implementación obvia no sea necesariamente de una línea o muy corta, pero en este caso, al implementar una función que sigue una definición matemática, es posible decir que es trivial plasmar esta definición en una función. | ||
|
||
De igual manera es importante mencionar que si conocemos la implementación obvia, hay que usarla y no hay necesidad de pasar por la implementación fake ni la triangulación. | ||
|
||
### Uno a Muchos | ||
|
||
Este patrón es el último. No aplica aquí, pues se utiliza cuando tenemos colecciones de datos y partimos del caso de un solo elemento y al final generalizamos para muchos. Pero es bueno conocerlo porque al entenderlo, resulta obvio que la solución de un problema de este tipo sea más fácil de alcanzar si tomamos un caso específico para construir el caso general. Esto es lo que se conoce en lógica como **inducción**. | ||
|
||
## Retos Encontrados | ||
|
||
- **Eficiencia de la Recursión.** La recursión es clara y directa, pero puede ser ineficiente para números grandes debido a cálculos redundantes. Considerar la memoización o una implementación iterativa es clave para mejorar el rendimiento. | ||
|
||
- **Cobertura Exhaustiva de Pruebas.** Asegurar que todas las variaciones, desde los casos base hasta números más altos, estén cubiertas por pruebas es crucial para la robustez del código. | ||
|
||
- **Refactorización Continua.** Mantener el código limpio y modular es esencial. Es común que inicialmente coloquemos implementaciones y pruebas en el mismo archivo, por lo que este proceso incluye mover la función `fibonacci` fuera del archivo que contiene las pruebas, además de optimizar la estructura del código. | ||
|
||
## Enlace al repositorio de GitHub | ||
|
||
Puedes encontrar esta kata, y el resto de ellas, [aquí](https://github.com/carlos-talavera/katas-sustainable-testing-ts). | ||
|
||
## Conclusión | ||
|
||
El ejercicio de implementar la serie de Fibonacci utilizando TDD nos proporciona una valiosa oportunidad para practicar patrones de desarrollo y enfrentar desafíos comunes en la programación. A través de la implementación fake, la triangulación, la implementación obvia y el enfoque de uno a muchos, no solo logramos una solución correcta, sino que también refinamos nuestra habilidad para pensar de manera sistemática y disciplinada. Es por eso que al aplicar estos patrones y prácticas, podemos abordar problemas complejos con confianza y claridad. | ||
|
||
Espero que este artículo te haya ayudado a entender estos patrones. Si tienes alguna duda o quieres compartir algo, déjalo en los comentarios :) |