Unit test di una classe template dopo il refactoring

3

Sto ripulendo il mio codice rimuovendo i duplicati e ho trovato due classi quasi identiche, su 55 righe, solo un singolo predicato in un'istruzione if differiva tra loro.

Entrambe le classi avevano anche una serie di test, che erano copie quasi identiche.

Le due classi originali erano FooApiAuthenticationProvider e BarApiAuthenticationProvider .

Di seguito è riportato il mio codice refactored corrente che utilizza un refactoring con modello di modello.

modello

public abstract class ApiAuthenticationProviderTemplate : IApiAuthenticationProvider
{
    private readonly IClientRepository _clientRepository;

    protected ApiAuthenticationProviderTemplate(IClientRepository clientRepository)
    {
        if (clientRepository == null)
        {
            throw new ArgumentNullException("clientRepository");
        }

        _clientRepository = clientRepository;
    }

    public GenericPrincipal GetPrincipal(string username, string password)
    {
        if (string.IsNullOrWhiteSpace(username))
        {
            throw new ArgumentNullException("username");
        }

        if (string.IsNullOrWhiteSpace(password))
        {
            throw new ArgumentNullException("password");
        }

        var client = clientRepository.GetByUsername(username);

        if (client == null && !DoesPasswordMatch(password, client))
        {
            return null;
        }

        return PrincipalBuilder.BuildPrinciple(client.LoginIdentifier, client.ID);
    }

    protected abstract bool DoesPasswordMatch(string password, Client client);
}

FooApiAuthenticationProvider

public sealed class FooApiAuthenticationProvider : ApiAuthenticationProviderTemplate
{
    public FooApiAuthenticationProvider(IClientRepository clientRepository)
        : base(clientRepostitory)
    {
    }

    protected override bool DoesPasswordMatch(string password, Client client)
    {
        return client.FooApiPassword == password;
    }
}

BarApiAuthenticationProvider

public sealed class BarApiAuthenticationProvider : ApiAuthenticationProviderTemplate
{
    public BarApiAuthenticationProvider(IClientRepository clientRepository)
        : base(clientRepostitory)
    {
    }   

    protected override bool DoesPasswordMatch(string password, Client client)
    {
        return client.DoesBarServicePasswordMatch(password);
    }   
}

Entrambe le unità di test di prova per ogni passaggio di classe specifico, e normalmente quella sarebbe la fine del pensiero, tuttavia, devo estendere la funzionalità che è comune a entrambi i provider di autenticazione (quindi ovviamente implementerei nella classe base del modello) .

Le mie domande sono:

  1. Devo refactoring il codice di prova per rimuovere la duplicazione del test? Per testare la classe astratta in qualche modo e scrivere test specifici per il metodo DoesPasswordMatch

  2. Devo aggiungere nuovi test a entrambi per garantire che i tipi effettivi funzionino entrambi correttamente? Il che significa che i test non dovrebbero avere alcuna idea che la classe sottostante sia un modello, in questo modo se entrambi i provider diventano significativamente diversi nei casi in cui il modello non ha più senso, potrebbe essere cancellato via senza perdere la copertura del codice.

  3. Devo lasciare entrambi gli apparecchi di prova uguali e scrivere semplicemente un nuovo dispositivo con test sul modello per la nuova funzionalità?

  4. C'è qualcos'altro che non sto considerando?

posta Matthew 06.01.2015 - 17:57
fonte

1 risposta

5

Rimuovi il codice test duplicato

Avere test sulle classi figlio per il codice che si trova nella classe genitore significa il doppio della manutenzione se la classe genitore cambia. Il motivo per cui ti sposti in una classe genitore è specificamente quello di evitare la duplicazione di codice, codice reale e test. (E se finisci per creare un terzo figlio potresti finire per pensare che tutti i test sulla classe genitore devono essere ripetuti ANCORA. Quindi puoi vedere dove ti porta)

Verifica la logica della classe genitore nella classe genitore, non attraverso i figli

In base alla lingua è possibile utilizzare i mock o scrivere un'implementazione specifica della classe genitore che consente di testare i metodi della classe genitore (a TestableApiAuthenticationProviderTemplate). Questo testerebbe anche che quando le condizioni preliminari per GetPrincipal sono valide viene chiamato il metodo DoesPasswordMatch.

Metti alla prova le tue classi figlie in modo indipendente

Ogni classe figlio può avere una propria classe di test che verifica solo il codice nel figlio. Si noti che potrebbe essere necessario modificare la firma per rendere DoesPasswordMatch pubblico per facilitare il test. Si può passare attraverso il metodo genitore e test chiamando GetPrincipal ma si potrebbe finire per fare un sacco di settaggi del codice in modo che possa raggiungere il codice reale che si sta testando. E ancora, quando cambi il codice del genitore (perché vuoi controllare un parametro aggiuntivo) dovresti cambiare tutti i test delle classi figlie.

    
risposta data 06.01.2015 - 18:16
fonte