Risoluzione dei problemi associati alla funzione diadica assertEquals (previsto, effettivo)

11

Dopo anni di codifica da cowboy, ho deciso di prendere un libro su come scrivere un codice di buona qualità. Sto leggendo Clean Code di Robert Cecil Martin. Nel capitolo 3 (funzioni) c'è una sezione sulle funzioni diadiche. Ecco un estratto dal libro.

Even obvious dyadic functions like assertEquals(expected, actual) are problematic. How many times have you put the actual where the expected should be? The two arguments have no natural ordering. The expected, actual ordering is a convention that requires practice to learn.

L'autore è un punto convincente. Lavoro nell'apprendimento automatico e mi imbatto continuamente in questo. Ad esempio, tutte le funzioni metriche nella libreria sklearn (probabilmente la libreria python più utilizzata nel campo) richiedono che si presti attenzione all'ordine degli input. Come esempio sklearn.metrics.homogeneity_score prende come input labels_true e% codice%. Ciò che fa questa funzione non è troppo rilevante, ciò che è rilevante è che se si cambia l'ordine degli input non verrà generato alcun errore. In effetti, il passaggio degli input equivale a utilizzare un'altra funzione nella libreria.

Tuttavia il libro non continua a dire una correzione ragionevole per funzioni come labels_pred . Non riesco a pensare a una correzione per assertEquals o per le funzioni che mi capita spesso di incontrare come quella sopra descritta. Quali sono le buone pratiche per risolvere questo problema?

    
posta HBeel 03.06.2017 - 14:26
fonte

2 risposte

11

È bene essere consapevoli di un possibile problema anche quando non c'è una soluzione - in questo modo puoi essere vigile quando leggi o scrivi questo codice. In questo specifico esempio, ci si abitua all'ordine degli argomenti dopo un po 'di tempo.

Esistono modi a livello di linguaggio per prevenire qualsiasi confusione sull'ordine dei parametri: argomenti con nome. Questo sfortunatamente non è supportato in molte lingue con la sintassi in stile C come Java o C ++. Ma in Python, ogni argomento può essere un argomento con nome. Invece di chiamare una funzione def foo(a, b) come foo(1, 2) , possiamo fare foo(a=1, b=2) . Molti linguaggi moderni come C # hanno una sintassi simile. La famiglia di linguaggi Smalltalk ha preso gli argomenti con nome più lontano: non ci sono argomenti posizionali e tutto è chiamato. Questo può portare a un codice che legge molto vicino al linguaggio naturale.

Un'alternativa più pratica è creare API che simulino gli argomenti con nome. Queste possono essere API fluide o funzioni di supporto che creano un flusso naturale. Il nome assertEquals(actual, expected) è confuso. Alcune alternative che ho visto:

  • assertThat(actual, is(equalTo(expected))) : avvolgendo alcuni argomenti nei tipi di helper, le funzioni di wrapping fungono effettivamente da nomi di parametri. Nel caso specifico delle asserzioni di test unitari, questa tecnica è utilizzata da Matchers di Hamcrest per fornire un sistema di asserzioni estendibile e componibile. Lo svantaggio qui è che si ottiene un sacco di nidificazione e che è necessario importare molte funzioni di supporto. Questa è la mia tecnica di go-in in C ++.

  • expect(actual).to.be(expected) : un'API fluente in cui stringa le chiamate di funzione insieme. Mentre questo evita l'annidamento extra, questo non è molto estendibile. Mentre trovo che le API fluenti leggano molto bene, progettare una buona API fluente tende a fare un sacco di sforzi nella mia esperienza, perché è necessario implementare classi aggiuntive per gli stati non terminali nella catena di chiamate. Questo sforzo si ripaga solo nel contesto di un IDE di completamento automatico che può suggerire le prossime chiamate di metodo consentite.

risposta data 03.06.2017 - 14:50
fonte
4

Ci sono diversi metodi per evitare questo problema. Uno che non ti obbliga a cambiare il metodo che chiami:

Invece di

assertEquals( 42, meaningOfLife() ); 

Usa

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

Questo costringe la convenzione a essere aperta dove è facile individuarli mentre vengono commutati. Certo non è così facile da scrivere ma è facile da leggere.

Se puoi cambiare il metodo chiamato puoi usare il sistema di battitura per forzare l'uso che è facile da leggere.

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

Alcune lingue ti permettono di evitarlo perché hanno parametri con nome:

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

Gli altri non li simulano così:

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

Qualunque cosa tu trovi, un modo che rende ovvio che è corretto quando letto. Non farmi indovinare quale sia la convenzione. Mostramelo.

    
risposta data 03.06.2017 - 14:51
fonte

Leggi altre domande sui tag