Resolver los problemas que vienen con la función diádica afirmar Igual (esperado, real)

10

Después de años de codificación de vaqueros, decidí recoger un libro sobre cómo escribir un código de buena calidad. Estoy leyendo Clean Code de Robert Cecil Martin. En el capítulo 3 (funciones) hay una sección sobre funciones diádicas. Aquí hay un extracto del libro.

Incluso funciones diádicas obvias como assertEquals(expected, actual)son problemáticas. ¿Cuántas veces has puesto el real donde debería estar lo esperado? Los dos argumentos no tienen un orden natural. El pedido real esperado es una convención que requiere práctica para aprender.

El autor hace un punto convincente. Trabajo en aprendizaje automático y me encuentro con esto todo el tiempo. Por ejemplo, todas las funciones métricas en la biblioteca sklearn (probablemente la biblioteca python más utilizada en el campo) requieren que tenga cuidado con el orden de las entradas. Como ejemplo, sklearn.metrics.homogeneity_score toma como entradas labels_truey labels_pred. Lo que hace esta función no es demasiado relevante, lo que es relevante es que si cambia el orden de las entradas no se generará ningún error. De hecho, cambiar las entradas es equivalente a usar otra función en la biblioteca.

Sin embargo, el libro no dice una solución sensata para funciones como assertEquals. No puedo pensar en una solución para assertEqualso para las funciones que a menudo encuentro como la descrita anteriormente. ¿Cuáles son las buenas prácticas para resolver este problema?

HBeel
fuente

Respuestas:

11

Es bueno estar al tanto de un posible problema, incluso cuando no hay solución, de esa manera puede estar atento al leer o escribir dicho código. En este ejemplo específico, solo te acostumbras al orden de los argumentos después de un tiempo.

Hay formas de nivel de lenguaje para evitar cualquier confusión sobre el orden de los parámetros: argumentos con nombre. Desafortunadamente, esto no se admite en muchos lenguajes con sintaxis de estilo C como Java o C ++. Pero en Python, cada argumento puede ser un argumento con nombre. En lugar de llamar a una función def foo(a, b)como foo(1, 2), podemos hacer foo(a=1, b=2). Muchos lenguajes modernos como C # tienen una sintaxis similar. La familia de idiomas Smalltalk ha llevado los argumentos con nombre más lejos: no hay argumentos posicionales y todo tiene nombre. Esto puede conducir a un código que se lee muy cerca del lenguaje natural.

Una alternativa más práctica es crear API que simulen argumentos con nombre. Estas pueden ser API fluidas o funciones auxiliares que crean un flujo natural. El assertEquals(actual, expected)nombre es confuso. Algunas alternativas que he visto:

  • assertThat(actual, is(equalTo(expected))): al ajustar algunos argumentos en tipos auxiliares, las funciones de ajuste sirven efectivamente como nombres de parámetros. En el caso específico de las afirmaciones de pruebas unitarias, esta técnica es utilizada por los investigadores de Hamcrest para proporcionar un sistema de afirmación extensible y composable. La desventaja aquí es que obtienes una gran cantidad de anidamiento y necesitas importar muchas funciones auxiliares. Esta es mi técnica de referencia en C ++.

  • expect(actual).to.be(expected): una API fluida donde encadena llamadas de función juntas. Si bien esto evita el anidamiento adicional, esto no es muy extensible. Si bien encuentro que las API fluidas se leen muy bien, diseñar una buena API fluida tiende a requerir mucho esfuerzo en mi experiencia, porque necesitas implementar clases adicionales para estados no terminales en la cadena de llamadas. Este esfuerzo solo realmente vale la pena en el contexto de un IDE de autocompletado que puede sugerir las siguientes llamadas de método permitidas.

amon
fuente
4

Existen varios métodos para evitar este problema. Uno que no te obliga a cambiar el método que llamas:

Más bien que

assertEquals( 42, meaningOfLife() ); 

Utilizar

expected = 42;
actual = meaningOfLife();
assertEquals(expected, actual);

Esto obliga a la convención a la intemperie, donde es fácil verlos cambiar. Claro que no es tan fácil de escribir, pero es fácil de leer.

Si puede cambiar el método al que se llama, puede usar el sistema de escritura para forzar el uso que es fácil de leer.

assertThat( meaningOfLife(), is(42) );

Algunos idiomas le permiten evitar esto porque tienen parámetros con nombre:

assertEquals( expected=42, actual=meaningOfLife() );

Otros no, así que los simulas:

assertEquals().expected(42).actual( meaningOfLife() );

Hagas lo que hagas, encontrarás una manera que haga evidente cuál es la correcta cuando se lee. No me hagas adivinar cuál es la convención. Muéstramelo

naranja confitada
fuente