Test dell'unità e dipendenze

2

Sto solo imparando come fare un corretto test delle unità, e molte risorse fanno riferimento all'uso di dipendenze esplicite, cioè dipendenze che vengono passate nel costruttore, per consentire facili simulazioni / stub, ecc.

Questo ha ovviamente un senso.

Ciò che non mi è chiaro è dove devono essere creati questi oggetti, e in che modo testare questi metodi?

Ad esempio, supponiamo tu abbia un ActionMethod di ASP.net. Questo fa uso di una classe che richiede 3 dipendenze, semplicemente "nuove" quelle in ActionMethod? In tal caso, in che modo testeresti quindi ActionMethod?

    
posta Dave Leonard 25.03.2015 - 14:35
fonte

3 risposte

1

Molto probabilmente questi oggetti sono creati da una fabbrica o da un singolo punto in cui il grafico dell'oggetto viene creato come indicato dai commenti di @ guillaume31. Se dalla fabbrica assomiglierebbe a questo:

namespace Repository.Lookup
{
    public class LookupRepositoryFactory
    {
        public static ILookupRepository Create()
        {
            ILookupRepository lookupRepository = new LookupRepository(); 

            return lookupRepository;
        }
    }
}

Quindi il test per la fabbrica:

namespace RepositoryTests.Lookup
{
    [TestClass]
    public class LookupRepositoryFactoryTests
    {
        [TestMethod]
        public void TestCreateLookupRepository()
        {
            var expectedType = typeof(LookupRepository);

            var repository = LookupRepositoryFactory.Create();

            Assert.IsInstanceOfType(repository, expectedType);
        }
    }
}

Quindi uso:

var lookupRepository = LookupRepositoryFactory.Create();

Poi lo hai appena passato al costruttore come hai detto, in questo modo:

namespace CommonLookupAPI
{
    public class GenericLookup
    {
        private readonly ILookupRepository _repository;

        public GenericLookup(ILookupRepository repository)
        {
            _repository = repository;
        }

        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            var commonLookups = _repository.GetCommonLookup(lineOfBusiness, classificationType, state, grouping, subGrouping);
            return commonLookups;
        }
    }
}

Ciò consente di prendere in giro il repository con una falsa implementazione in modo da poter testare la ricerca generica senza utilizzare il repository reale.

Ecco i test:

namespace CommonLookupAPITests
{
    [TestClass]
    public class GenericLookupTests
    {
        [TestMethod]
        public void TestGenericLookupReturnsLookup()
        {
            ILookupRepository repository = MockRepositories.CreateMockSuccessRepository();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);

            Assert.AreEqual(lookups.Count, 0);
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void TestGenericLookupReturnsFailUnExpected()
        {
            ILookupRepository repository = MockRepositories.CreateMockFailRepositoryException();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);
        }

        [TestMethod]
        [ExpectedException(typeof(DataAccessException))]
        public void TestGenericLookupReturnsFailDataAccess()
        {
            ILookupRepository repository = MockRepositories.CreateMockFailRepositoryDataAccessException();
            var genericLookup = new GenericLookup(repository);

            var lookups = genericLookup.GetCommonLookup(LineOfBusiness.AssistedLivingProfessionalLiability, ClassificationType.ClaimLimit, State.NotDefined, Grouping.NotDefined, SubGrouping.NotDefined);
        }
    }
}

E infine i Repository di simulazione utilizzati dai test:

namespace CommonLookupAPITests
{

    public class MockRepositories
    {
        public static ILookupRepository CreateMockSuccessRepository()
        {
            ILookupRepository repository = new MockSuccessRepository();
            return repository;
        }

        public static ILookupRepository CreateMockFailRepositoryException()
        {
            ILookupRepository repository = new MockFailRepositoryException();
            return repository;
        }

        public static ILookupRepository CreateMockFailRepositoryDataAccessException()
        {
            ILookupRepository repository = new MockFailRepositoryDataAccessException();
            return repository;
        }
    }

    public class MockSuccessRepository : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            var commonLookup = new List<CommonLookup>();
            return commonLookup;
        }
    }

    public class MockFailRepositoryException : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            throw new Exception();
        }
    }

    public class MockFailRepositoryDataAccessException : ILookupRepository
    {
        public List<CommonLookup> GetCommonLookup(LineOfBusiness lineOfBusiness, ClassificationType classificationType,
            State state, Grouping grouping, SubGrouping subGrouping)
        {
            throw new DataAccessException();
        }
    }
}
    
risposta data 25.03.2015 - 14:52
fonte
2

Non conosco ASP.NET, ma generalmente le tue dipendenze sarebbero sulle interfacce e nei tuoi test di unità fornisci finte, stub, o false implementazioni di quelle interfacce. Questo ti permette di testare la tua classe in isolamento, dal momento che l'unico codice sotto test è la classe stessa oltre alle tue dipendenze "scheletro".

    
risposta data 25.03.2015 - 14:48
fonte
0

What isn't clear to me is where should these objects be created

Ovunque tranne che nell'oggetto dipendente stesso, ovviamente. Il punto è che l'oggetto consumatore non controlla il ciclo di vita della sua dipendenza, come fa qualcun altro.

Un possibile approccio globale a questo è di comporre l'intero grafo di oggetti dell'applicazione in un unico punto, la Root di composizione , situato il più vicino possibile al punto di ingresso dell'applicazione. Ciò che fa concretamente è la novità di tutti gli oggetti che sono prevedibili in quel momento e li collegano usando i costruttori.

As an example, say you have an ASP.net ActionMethod. This makes use of a class that requires 3 dependencies, would you simply 'new' those up in the ActionMethod?

Ovviamente no - ActionMethod non sa come viene costruita la classe che usa, per non parlare di come vengono aggiornate le dipendenze di questa dipendenza:)

how then would you test that ActionMethod?

Come hai detto, passando mock o stub nel costruttore del Controller o come argomenti al ActionMethod stesso.

    
risposta data 25.03.2015 - 16:02
fonte

Leggi altre domande sui tag