Test delle unità e riutilizzo della classe estratta

2

Attualmente ho 3 classi di servizio, tutte hanno un modello simile e ho bisogno di creare una quarta classe di servizio che amalgami la funzionalità degli altri tre.

Tre classi attuali

public class PersonService : IPersonService
{
    public PersonServiceResult CreatePerson(CreatePersonParams createPersonParams)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            // [code]

            unitOfWork.Commit();
            return new PersonServiceResult(...);
        }
    }
}

public class OrderService : IOrderService
{
    public OrderServiceResult CreateOrder(
        int personId, 
        IEnumerable<PurchaseItem> items)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            // [code]

            unitOfWork.Commit();
            return new OrderServiceResult(...);
        }
    }
}

public class PaymentProcessingService : IPaymentProcessingService
{
    public PaymentResult ProcessPayment(int orderId, CreditCard creditCard)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            // [code]

            unitOfWork.Commit();
            return new PaymentResult(...);              
        }
    }
}

Ora ho bisogno di usare il comportamento in tutte e 3 queste classi in una unità di lavoro , ho ancora bisogno che siano presenti le altre 3 classi per altre dipendenze nel progetto.

Il mio primo pensiero sarebbe quello di estrarre una classe per ciascuno dei 3 servizi che prende come argomento una unità di lavoro.

Estrazione del comportamento dalle classi correnti

public class PersonCreator
{
    public CreatePersonResult CreatePerson(
        IUnitOfWork unitOfWork, 
        CreatePersonParams createPerson)
    {
        // [code]
    }
}

public class PersonService : IPersonService
{
    private readonly PersonCreator Creator = new PersonCreator();

    public PersonServiceResult CreatePerson(CreatePersonParams createPersonParams)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            var result = Creator.CreatePerson(unitOfWork, createPersonParams);

            unitOfWork.Commit();
            return new PersonServiceResult(result);
        }
    }       
}

Proposta nuova classe utilizzando il comportamento estratto

public class QuickOrderService : IQuickOrderService
{
    private readonly PersonCreator PersonCreator = new PersonCreator(); 
    private readonly OrderCreator OrderCreator = new OrderCreator();    
    private readonly PaymentProcessor PaymentProcessor = new PaymentProcessor();    

    public QuickOrderResult CreateQuickOrder(
        CreatePersonParams createPersonParams,
        IEnumerable<PurchaseItem> items,
        CreditCard creditCard)
    {
        using (var unitOfWork = _unitOfWorkFactory.Create())
        {
            var person = PersonCreator.CreatePerson(unitOfWork, createPersonParams);
            var order = OrderCreator.CreateOrder(unitOfWork, person.Id, items);
            var payment = PaymentProcessor.Process(unitOfWork, order.Id, creditCard);

            unitOfWork.Commit();

            return QuickOrderResult(...);
        }
    }
}

Funziona bene, ma sono preoccupato di testare questa nuova classe.

Domande sulla proposta

  1. Duplica la maggior parte dei test per la classe QuickOrderService
  2. Non testare questa classe poiché la maggior parte di essa è coperta da altri test
  3. Inietti PersonCreator , OrderCreator e PaymentProcessor in QuickOrderService invece di installarli e provare solo che QuickOrderService chiama correttamente le dipendenze

Anche se facendo tutto questo excersise mi viene in mente che potrebbe esserci una soluzione migliore che riduce la duplicazione mantenendo la copertura del codice.

    
posta Matthew 08.04.2015 - 17:08
fonte

2 risposte

2

Vorrei iniziare eliminando il codice duplicato prima costruendo un servizio generico di creazione, qualcosa sulla falsariga di

   class GenericCreatorService<T>
   {
        UnitOfWorkFactory _unitOfWorkFactory;

        // ...

        public T Create(Func<T,UnitOfWork> func)
        {
           using (var unitOfWork = _unitOfWorkFactory.Create())
           {
                T result = func(unitOfWork);
                unitOfWork.Commit();
                return result;
           }
        }
   }

Ora hai solo bisogno di testare l'unità una volta . Riutilizzalo per implementare le tue classi di servizio originali, ad esempio

public class PersonService : IPersonService
{
    private readonly PersonCreator Creator = new PersonCreator();

    public PersonServiceResult CreatePerson(CreatePersonParams createPersonParams)
    {
        var gs = new GenericCreatorService<PersonServiceResult>(...);
        return gs.Create( uow => PersonCreator.CreatePerson(uow,createPersonParams))
    }       
}

Dovresti concentrarti sul test delle singole classi Creator , un test per ciascuna, ed è discutibile se hai davvero bisogno di testare unitariamente le singole classi di servizio, poiché contengono solo un codice di orchestrazione molto semplice. Se ritieni di aver bisogno di un test extra per il tuo QuickOrderService perché la vera implementazione è più complicata, puoi comunque fare ciò che @JDT ha suggerito e usare DI per gli altri creativi.

    
risposta data 08.04.2015 - 23:03
fonte
1

Inietti le tre classi Creator e testane le chiamate smontando il QuickOrderService. Ciò mantiene i test sulle classi rilevanti.

Inoltre, potresti non aver bisogno di trascinare in giro l'unitOfWork. L'implementazione di Crea potrebbe essere modificata in modo da creare semplicemente una nuova transazione se non ne esiste alcuna e in caso contrario restituire la transazione esistente. Ciò potrebbe tenere traccia della quantità di chiamate a Crea e impegna e solo impegnare la transazione quando l'ultima transazione richiesta è stata commessa. In questo momento hai tre servizi che prendono un'unità di Lavoro e uno che non lo fa. Cerca coerenza per il tuo progetto e passa TUTTI i servizi a unitOfWork o chiedi al servizio di gestire unitOfWork per se stesso.

    
risposta data 08.04.2015 - 22:18
fonte

Leggi altre domande sui tag