TestInvariant: TDD y DbC
Aunque siguen habiendo muchos debates si Design By Contract (DbC) es mejor que Test Driven Development (TDD), o al reves. El otro día me tope con un post de Martin Fowler, que mostraba una idea que mezclaba la invariante de DbC en TDD.
Una invariante indica una propiedad que no variara (que no cambiara) que siempre será verdadera en una clase, por ejemplo podríamos tener una clase Empleado, donde el salario siempre sea mayor o igual al salario permitido por el país (por ejemplo en Perú, 950 soles).
class Employee {
// salary >= basic_salary_allowed_by_the_country
Money salary;
// otros atributos, métodos...
}
Algunos lenguajes, como Eiffel permiten especificar la invariante de una clase y no solo eso sino que automaticamente chequean que está se cumpla siempre. Un fallo de ella, lanzaría una excepción.
Aplicando está idea a TDD, significa que deberiamos tener un método que pruebe la invariante y usarlo en nuestras pruebas. Por ejemplo:
class Employee {
Money basic_salary_allowed_by_the_country = Money.of(950, SOLES); // En Perú
Money salary;
public boolean passesInvariant() {
return salary >= basic_salary_allowed_by_the_country
}
}
Luego si tuvieramos que probar un método del Empleado, como agregar un bono (addBono
), podriamos verificar que la invariante sigua cumpliendose antes y despúes de llamar al método.
class Empleado {
// ...
public void addBono(bono) {
salary += bono;
}
}
// En EmpleadoTest
public void testAddBono() {
Employee arthur = new Employee("Arthur", Money.of(2000, SOLES));
assert arthur.passesInvariant()
arthur.addBono(200)
assert arthur.passesInvariant()
assertEquals(Money.of(2200, SOLES), arthur.salary)
}
Claramente si pasamos como parametro un bono negativo muy grande, la invariante puede ser rota. Aquí, es donde entraría en utilidad está idea de TestInvariant, al avisarnos que algo está mal en addBono
.
Cambios y revisiones:
02/11/2019: fix typos
02/11/2019: add new test invariant post