Come tutti sappiamo con l’avvento di .Net Core tutto l’ecosistema sta subendo evoluzioni, fin dalla prima versione del progetto ASP.NET MVC Core è stato scelto come sistema di testing XUnit.

xunit-dot-net-full-logo

Per poter utilizzare XUnit da riga di comando dobbiamo aggiungere il pacchetto nuget “xunit” al nostro progetto.

<ItemGroup>
	<PackageReference Include="xunit" Version="2.2.0" />
</ItemGroup>
  

mentre per poter utilizzare xunit all’interno di Visual Studio  dovremo aggiungere i seguenti pacchetti nuget (in questo articolo eseguiremo i test direttamente da VisualStudio).

<ItemGroup>
	<PackageReference Include="xunit" Version="2.2.0" />
	<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
  

Con XUnit, quando scriviamo il nostro test, abbiamo la possibilità di scegliere tra 2 differenti tipi di Unit Test, Fact e Theory.

I test di tipo Fact sono i classici test che siamo abituati a vedere,

[Fact] public void MemoryRepository_PersonById_foundValue() { //Arrange var memoryContext = new MemoryRepository(); memoryContext.InsertPerson(new Person() { ID = 1, Code="001", Name="Person One", Role="User" }); memoryContext.InsertPerson(new Person() { ID = 2, Code = "002", Name = "Person Two", Role = "Admin" }); memoryContext.InsertPerson(new Person() { ID = 3, Code = "003", Name = "Person Three", Role = "Owner" }); //Act var person = memoryContext.PersonById(1); //Assert Assert.NotNull(person); Assert.Equal("Person One", person.Name); }

L’esempio sopra, testa della classe MemoryRepository il metodo PersonById che deve filtrare per id.

Andiamo ad analizzare nel dettaglio il test, così come siamo stati abituati nel tempo possiamo vedere la sezione Arrange dove  andiamo ad inizializzare tutte le classi che utilizzeremo nel test; la sezione di Act dove eseguiremo i metodi oggetto del test ed in fine la sezione di Assert dove andremo ad eseguire i controlli del corretto risultato del test, che nel nostro caso non deve essere null ed il nome deve essere “Person One”.

Per poter eseguire i test all’interno di Visual Studio dobbiamo per prima cosa compilare il progetto in modo che tutte le classi di test siano compilate a questo punto aprendo la vista Test Explorer possiamo vedere tutti i test che possiamo avviare.

TestExplorerWindow

Individuato all’interno della lista il test che vogliamo eseguire facciamo click destro e selezioniamo “Run Selected Test” (ovviamente se la nostra necessità è quella di avviare il test come debug per verificare eventuali bug, possiamo avviare il test selezionando la voce “Debug Selected Test”).

RunTest

Al termine del test possiamo vedere il risultato

TestCompleted

Ovviamente il test eseguito è molto semplice ed xunit mette a disposizione tutta una serie di features molto interessanti; ad esempio per testare se una determinata eccezione viene avviata possiamo fare in questo modo

[Fact] public void ResponseToFileAttributeResponse_onExecuted_PathSpecified()

{ //Arrange var attribute = createAttribute("txt", "FILENAME", false, false, Path.GetTempPath()); var context = createBatchExecutionContext(Guid.NewGuid(), new byte[] { 123, 221, 213, 128, 165 }); //Act var ex = Record.Exception(() => attribute.onExecuted(context)); //Assert Assert.Null(ex); }

Testando la variabile ex siamo in grado di capire se l’eccezione è stata lanciata.

Ipotizziamo che il nostro test prevede dei valori di input, così come visto fin’ora dovremmo create tanti metodi di test [Fact] quante sono le combinazioni da testare.
XUnit ci mette a disposizione l’attributo [Theory], quando marchiamo un test come theory stiamo dicendo ad XUnit che questo test prevede dei dati di input che andremo a specificare utilizzando 1 o più attributi [InlineData], di seguito un esempio.

[Theory]
[InlineData("txt", "FILENAME",false, false, "", "FILENAME.txt", "ce8a738b-2534-4060-9f1c-9da6eba4aef8")]
[InlineData("txt", "FILENAME", true, false, "", "FILENAME-ce8a738b-2534-4060-9f1c-9da6eba4aef8.txt", "ce8a738b-2534-4060-9f1c-9da6eba4aef8")]
public void ResponseToFileAttributeResponse_FullFileName(string fileExtention, string fileName, bool sessionIdInFileName, bool timeStampToken,  string path, string expected, string sessionId)
{
        //Arrange
        var attribute = createAttribute(
fileExtention, fileName, sessionIdInFileName,
timeStampToken, path);
var context = createBatchExecutionContext(
new Guid(sessionId), "TEST TEST TEST"); //Act attribute.onExecuted(context); //Assert Assert.Equal(expected, attribute.FullFileName); }

Così facendo XUnit eseguira N volte il test, dove N sono le [inlineData] che sono state configurate. Questo ci permette di scrivere dei test configurabili scrivendo test piu generici basati su dei parametri.

Andiamo ora a fare un esempio che sicuramente si accosta di più al mondo reale, ipotizziamo di avere se suguenti classi :

Una classe repository di accesso ai dati che implementa la seguente interfaccia

public interface IDataRepository
{
    List<Person> AllPersons();
    Person PersonById(int id);
    List<Person> PersonByCode(string code);
    List<Person> PersonByRole(string role);
    bool InsertPerson(Person person);
    bool UpdatePerson(Person person);
    bool RemovePerson(Person person);
}
  

Una classe Provider di generazione degli accorpamenti dei dati

public class StatisticsProvider
{
    private IDataRepository _dataRepository = null;
    public StatisticsProvider(IDataRepository dataRepository)
    {
        _dataRepository = dataRepository;
    }

    public List<StstisticsByRoleModel> ByRole()
    {
        var result = (from d in _dataRepository.AllPersons()
                        group d by d.Role into grp
                        select new StstisticsByRoleModel()
                        {
                            Role = grp.Key,
                            Count = grp.Count()
                        }
            ).ToList();

        return result;
    }
}
  

alla quale passiamo la nostra classe Repository di accesso ai dati utilizzando la dependency injection.

Come possiamo testare il corretto funzionamento della classe StatisticsProvider ?

Semplice, moqando la classe repository; per fare questo possiamo utilizzare una libreria chiamata Moq che possiamo includere direttamente da Nuget.

Moq e in grado di definire un interfaccia in modo da restituire dei dati prefissati (appunto moqati) come risposta di un metodo, ecco come

var dataRepository = new Mock<IDataRepository>(MockBehavior.Strict);
dataRepository.Setup(s => s.AllPersons())
        .Returns(Persons)
        .Verifiable();

  

In queste righe di codice stiamo configurando Moq in modo che

  • Implementa ll tipo IDataRepository (la nostra interfaccia del data repository),
  • Alla chiamata del metodo AllPersons() rispondere con il contenuto della variabile Persons.
  • Deve essere verificato che la chiamata al metodo AllPersons() sia eseguita

A questo punto continuiamo inizializzando la nostra classe StatisticsProvider alla quale andiamo a passare nel costruttore l’implementazione moqata dell’interfaccia IDataRepository

var statProvider = new StatisticsProvider(dataRepository.Object);
  

quindi il nostro testo completo sarà così fatto

[Fact]
public void StatisticsProviderTest_ByRole()
{
    //Arrange
    var Persons = new List<Person>()
    {
        new Person()
        {
            ID=1, Code="001", Name="Person One", Role="User"
        },
        new Person()
        {
            ID=2, Code="002", Name="Person Two", Role="Admin"
        }
    };

    var dataRepository = new Mock<IDataRepository>(MockBehavior.Strict);
    dataRepository.Setup(s => s.AllPersons())
        .Returns(Persons)
        .Verifiable();

    var statProvider = new StatisticsProvider(dataRepository.Object);

    //Act
    var response = statProvider.ByRole();

    //Assert
    Assert.NotNull(response);
    Assert.Equal(2, response.Count);
    Assert.Collection(response,
        e1 =>
        {
            Assert.Equal("User", e1.Role);
            Assert.Equal(1, e1.Count);
        },
        e2 =>
        {
            Assert.Equal("Admin", e2.Role);
            Assert.Equal(1, e2.Count);
        });

    var ex = Record.Exception(() => dataRepository.Verify());
    Assert.Null(ex);
}

come si puo notare nella sezione Assert viene eseguito il metodo Verify() della classe moqata. Questo metodo implementato da moq controlla che tutti i setup configurati come Verifiable siano stati chiamati, in caso contrario si genera un’eccezione; nel nostro caso viene verificato che sia chiamato il metodo AllPersons().

In questo articolo abbiamo visto come implementare test utilizzando xUnit e Moq, sperando che sia stata una lettura piacevole  vi do appuntamento con il prossimo articolo dove continueremo ad approfondire le novità dell’ecosistema .Net Core.

Happy Coding.

Autore:


blog comments powered by Disqus

Calendar

<<  December 2017  >>
MonTueWedThuFriSatSun
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

View posts in large calendar

Category list