{"id":2256,"date":"2020-07-02T03:35:00","date_gmt":"2020-07-02T03:35:00","guid":{"rendered":"https:\/\/cloudsurfers.it\/?p=2256"},"modified":"2020-07-02T13:02:11","modified_gmt":"2020-07-02T13:02:11","slug":"unit-testing-net-core-ed-angular","status":"publish","type":"post","link":"https:\/\/cloudsurfers.it\/index.php\/unit-testing-net-core-ed-angular\/","title":{"rendered":"Unit Testing: concetti e soluzioni per .NET Core ed Angular"},"content":{"rendered":"\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"600\" class=\"wp-image-2263\" style=\"width: 1200px;\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header.png\" alt=\"\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header.png 1200w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header-300x150.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header-1024x512.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header-768x384.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/unit-test-header-600x300.png 600w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/p>\n\n\n\n<p>Unit Testing \u00e8 il nome dato ad una metodologia di test del software che aiuta a determinare se i moduli di un programma funzionano correttamente.<\/p>\n\n\n\n<p>Data questa definizione, \u00e8 abbastanza facile capire l&#8217;importanza di suddividere ed isolare correttamente il codice in piccole unit\u00e0, con pochi input e un singolo output, cos\u00ec da ottenere componenti adatti al riutilizzo ed al testing. Nella programmazione ad oggetti (OOP), in cui il codice sorgente del programma \u00e8 suddiviso in classi, un&#8217;unit\u00e0 lo \u00e8 spesso un metodo di una classe o pu\u00f2 anche essere una funzione statica di una classe di supporto.<\/p>\n\n\n\n<h1>Strategie di Unit Testing:<br>STD vs TDD vs BDD<\/h1>\n\n\n\n<p><strong>STD<\/strong>&nbsp;(<em>Standard Testing Development<\/em>), questo approccio prevede che prima venga scritto il codice e poi (forse) i test. In altre parole, il nostro codice sorgente pu\u00f2 essere (e quindi solitamente viene) scritto prima (o anche senza) test case.<\/p>\n\n\n\n<p><strong>TDD<\/strong>&nbsp;(<em>Test Driven Development<\/em>) \u00e8 pi\u00f9 una pratica di programmazione che un approccio di test e pu\u00f2 essere un\u2019ottima pratica, almeno per determinati scenari. In breve, uno sviluppatore che adotta la metodologia TDD convertir\u00e0 tutti i requisiti software in test case specifici, quindi scriver\u00e0 il nuovo codice (o migliorer\u00e0 il codice esistente) in modo che i test vengano superati.<br>La differenza principale tra i STD e TDD \u00e8 che in TDD i test sono condizioni obbligatorie che dobbiamo soddisfare, mentre nel caso STD sono principalmente la prova del funzionamento del nostro codice esistente.<br>Di seguito uno schema riassuntivo di quali sono i 3 step che caratterizzano TDD:<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/TDD.png\" alt=\"\" class=\"wp-image-2282\" width=\"300\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/TDD.png 568w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/TDD-246x300.png 246w\" sizes=\"(max-width: 568px) 100vw, 568px\" loading=\"lazy\" \/><\/figure><\/div>\n\n\n\n<p><strong>BDD<\/strong>&nbsp;(<em>Behavior-Driven Development<\/em>) \u00e8 un processo di sviluppo software che condivide lo stesso approccio test-first di TDD, ma concentrando i test su quella che dovr\u00e0 essere la prospettiva dell\u2019utente finale anzich\u00e9 sull\u2019implementazione. Se vogliamo testare l\u2019implementazione effettiva dei nostri metodi\/unit\u00e0, TDD potrebbe essere la strada giusta da percorrere. Tuttavia, se puntiamo a dei test pi\u00f9 orientati all\u2019utilizzo dell&#8217;utente finale, il solo approccio TDD potrebbe dare dei falsi positivi, specialmente se il sistema evolve (come spesso fanno i progetti guidati da Agile).<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/BDD.png\" alt=\"\" class=\"wp-image-2289\" width=\"350\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/BDD.png 748w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/BDD-300x265.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/BDD-600x529.png 600w\" sizes=\"(max-width: 748px) 100vw, 748px\" loading=\"lazy\" \/><\/figure><\/div>\n\n\n\n<p>TDD ha lo scopo di imporre agli sviluppatori il controllo sul codice scritto, mentre BDD mira a soddisfare sia lo sviluppatore che l\u2019utente finale.<br>Pertanto, possiamo concludere che BDD estende TDD e non lo sostituisce.<\/p>\n\n\n\n<h1>Tutorial: Unit Testing<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Backend &#8211; .NET Core<\/h2>\n\n\n\n<p>Partendo dal back-end, prenderemo in esame un esempio di come poter realizzare un server sviluppato in .NET Core utilizzando come framework di test&nbsp;<strong>xUnit.net<\/strong>.<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Creare un progetto di test di tipo Web Application .NET Core utilizzando il template Angular. Usiamo come nome del progetto ad esempio&nbsp;<em>UnitTestSample<\/em><br><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"680\" class=\"wp-image-2318\" style=\"width: 1024px;\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/VS2019-create-webapp.gif\" alt=\"\"><\/li><li>Aprire un prompt dei comandi e posizionarsi nella cartella root della solution<\/li><li>Digitare il seguente comando per creare un progetto di test vuoto<br><code>dotnet new xunit -o UnitTestSample.Tests<\/code><br>In questo modo .NET CLI creer\u00e0 per noi un nuovo progetto di test e si occuper\u00e0 di svolgere alcune operazioni di post process dopo la creazione.<\/li><li>Tornare in Visual Studio ed eliminare il file <em>UnitTest1.cs<\/em> perch\u00e9 andremo poi a creare le classi che serviranno.<\/li><li>Referenziare nel progetto di test creato il pacchetto <strong>Moq<\/strong><br><code>Install-Package Moq<\/code><\/li><li>Referenziare nel progetto di test creato il pacchetto <strong><strong>Microsoft.EntityFrameworkCore.InMemory<\/strong><\/strong><br><code>Install-Package Microsoft.EntityFrameworkCore.InMemory<\/code><\/li><\/ol>\n\n\n\n<p><strong>Moq<\/strong>: \u00e8 un <em>mocking<\/em> framework, ovvero \u00e8 uno strumento che permette di creare oggetti sostitutivi che simulino il comportamento di quelli reali. Il concetto di mocking quindi \u00e8 strettamente legato allo unit testing.<\/p>\n\n\n\n<p><strong>Microsoft.EntityFrameworkCore.InMemory<\/strong> \u00e8 un database provider per Entity Framework Core che pu\u00f2 essere utilizzato a scopo di test in quanto inizializza una base dati che rimane solamente in memoria, di fatto risulta essere un finto database<\/p>\n\n\n\n<p>Una volta terminata la configurazione dell\u2019ambiente, possiamo procedere con l\u2019implementazione dei test. Come prima cosa, aggiungere al progetto UnitTestSample.Test la dipendenza a UnitTestSample.<\/p>\n\n\n\n<p>Nel mondo Web API, quello che solitamente viene testato sono i metodi esposti dai Controller, ovvero le chiamate che vengono esposte all\u2019esterno. Le API per\u00f2 sfruttano due importanti dipendenze solitamente, che sono l\u2019<em>HttpContext<\/em> e l\u2019<em>ApplicationDbContext<\/em>. Supponiamo di voler testare un\u2019API <em>GetCar()<\/em> che ha come funzione quella di ritornare una citt\u00e0. Per scrivere questo test, creo un nuovo file <em>CarsController_Test.cs<\/em> all\u2019interno del progetto di Test ed inizializzo la classe nel modo seguente:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class CarsController_Test\n{\n\t\/\/\/ &lt;summary&gt;\n\t\/\/\/ Test GetCar() method\n\t\/\/\/ &lt;\/summary&gt;\n\t\/\/\/ &lt;returns&gt;&lt;\/returns&gt;\n\t&#x5B;Fact]\n\tpublic async void GetCar()\n\t{\n\t\t#region Arrange\n\t\t\/\/todo: define required assets\n\t\t#endregion\n\n\t\t#region Act\n\t\t\/\/todo: invoke the test\n\t\t#endregion\n\n\t\t#region Assert\n\t\t\/\/todo: verify that conditions are met\n\t\t#endregion\n\t}\n}\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\"><li><strong>Arrange<\/strong>: definisce le risorse necessarie per eseguire il test<\/li><li><strong>Act<\/strong>: richiama il comportamento del soggetto di test<\/li><li><strong>Assert<\/strong>: verifica che le condizioni previste siano soddisfatte valutando il valore di ritorno o misurandolo rispetto ad alcune regole definite dall&#8217;utente<\/li><\/ul>\n\n\n\n<p>Vediamo come popolare le tre region affinch\u00e8 venga popolato il context EntityFramework con un record di Car, il tutto senza andare per\u00f2 ad utilizzare il database finisco, ma lasciandolo solo in memoria.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class CarsController_Test\n{\n\t\/\/\/ &lt;summary&gt;\n\t\/\/\/ Test GetCar() method\n\t\/\/\/ &lt;\/summary&gt;\n\t\/\/\/ &lt;returns&gt;&lt;\/returns&gt;\n\t&#x5B;Fact]\n\tpublic async void GetCar()\n\t{\n\t\t#region Arrange\n\t\tvar options = new DbContextOptionsBuilder&lt;ModelContext&gt;()\n\t\t\t.UseInMemoryDatabase(databaseName: &quot;cars&quot;)\n\t\t\t.Options;\n\n\t\tusing (var ctx = new ModelContext(options))\n\t\t{\n\t\t\tawait ctx.Car.AddAsync(new Car\n\t\t\t{\n\t\t\t\tCarId = 1,\n\t\t\t\tBrand = &quot;Ferrari&quot;,\n\t\t\t\tModel = &quot;Fxx&quot;,\n\t\t\t\tCv = 1050\n\t\t\t});\n\t\t\tawait ctx.SaveChangesAsync();\n\t\t}\n\t\tCar existingCar = null;\n\t\tCar non_existingCar = null;\n\t\t#endregion\n\n\t\t#region Act\n\t\tusing (var ctx = new ModelContext(options))\n\t\t{\n\t\t\tvar controller = new CarsController(ctx);\n\t\t\texistingCar = await controller.GetCar(1);\n\t\t\tnon_existingCar = await controller.GetCar(2);\n\n\t\t}\n\t\t#endregion\n\n\t\t#region Assert\n\t\tAssert.True(existingCar != null &amp;&amp; non_existingCar == null);\n\t\t#endregion\n\t}\n}\n<\/pre><\/div>\n\n\n<p>Ora \u00e8 possibile mandare in esecuzione il test appena scritto; ci sono due modi per farlo:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li><strong>.NET Core CLI<\/strong>: Da riga di comando, posizionarsi nella cartella contenente il progetto di Test appena scritto e digitare il comando <code>dotnet test<\/code><\/li><li><strong>Visual Studio Test Explorer<\/strong>: Tramite la finestra Test Explorer di Visual Studio \u00e8 possibile visualizzare l\u2019elenco dei test scritti, avviarli e consultare l\u2019esito dopo averli eseguiti.<\/li><\/ol>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/test-explorer.png\" alt=\"\" class=\"wp-image-2426\" width=\"350\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/test-explorer.png 586w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/test-explorer-300x133.png 300w\" sizes=\"(max-width: 586px) 100vw, 586px\" loading=\"lazy\" \/><\/figure><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Frontend &#8211; Angular<\/h2>\n\n\n\n<p>Partendo dagli stessi principi elencati in precedenza, anche per la parte del front-end possiamo pensare di utilizzare esattamente lo stesso tipo di approccio per andare a testare la nostra app.<br>Gli strumenti da utilizzare per raggiungere l\u2019obiettivo sono:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/jasmine.github.io\/\" target=\"_blank\">Jasmine<\/a><\/strong>: testing framework Javascript che supporta perfettamente l\u2019approccio di tipo BDD.<\/li><li><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/karma-runner.github.io\/\" target=\"_blank\">Karma<\/a><\/strong>: strumento che permette di creare delle istanze del browser in cui avviare i test scritti con Jasmine e consultarne l\u2019esito<\/li><li><strong><a rel=\"noreferrer noopener\" href=\"https:\/\/www.protractortest.org\/\" target=\"_blank\">Protractor<\/a><\/strong>: framework di tipo end-to-end che permette di testare le applicazioni Angular all\u2019interno di un browser, simulando un\u2019interazione simile a quella che avrebbe un utente reale.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Karma<\/h3>\n\n\n\n<p>I Karma test vengono scritti sfruttando il framework Jasmine e solitamente sono costruiti utilizzando queste 3 principali API:<\/p>\n\n\n\n<ol class=\"wp-block-list\" type=\"1\"><li><strong>describe()<\/strong>: contesto in cui vengono rinchiuse una suite di test<\/li><li><strong>it()<\/strong>: dichiarazione di un singolo test<\/li><li><strong>expect()<\/strong>: il risultato atteso da un test<\/li><\/ol>\n\n\n\n<p>Queste API sono utilizzabili all\u2019interno dei files<em> *.spec.ts<\/em>.<br>Supponiamo di voler testare un componente <em>CarsComponent<\/em> (definiamo il nome del file del componente con <em>cars.component.ts<\/em>), quello che ci sar\u00e0 da fare in primis sar\u00e0 creare un nuovo file di test <em>cars.component.spec.ts<\/em>. Uno scheletro \u201ctipo\u201d sar\u00e0 come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\ndescribe(&#039;CarsComponent&#039;, () =&gt; {\n    let fixture: ComponentFixture &lt;CarsComponent&gt; ;\n    let component: CarsComponent;\n\n    \/\/ async beforeEach(): testBed initialization\n    beforeEach(async (() =&gt; {\n        \/\/todo: initialize required providers\n        TestBed.configureTestingModule({\n            declarations: &#x5B;CarsComponent],\n            imports: &#x5B;\n                BrowserAnimationsModule\n            ],\n            providers: &#x5B;\n                \/\/todo: reference required providers\n            ]\n        }).compileComponents();\n    }));\n\n    \/\/ synchronous beforeEach(): fixtures and components setup\n    beforeEach(() =&gt; {\n        fixture = TestBed.createComponent(CarsComponent);\n        component = fixture.componentInstance;\n        \/\/todo: configure fixture\/component\/children\/etc.\n    });\n    \/\/todo: implement some tests\n});\n<\/pre><\/div>\n\n\n<p>Alcune note riguardo questo schema:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Tutto il set di test del componente CarsComponent \u00e8 definito all\u2019interno di un unico describe;<\/li><li>La property <strong><em>fixture<\/em><\/strong> contiene un\u2019istanza di CarsComponent con il suo stato. Tramite fixture posso interagire con il componente ed i suoi child elements;<\/li><li>La property <strong><em>component<\/em><\/strong> contiene l\u2019istanza vera e propria del componente<\/li><li>Metodo <strong><em>async beforeEach()<\/em><\/strong> in cui so che TestBed \u00e8 stato creato e inizializzato<\/li><li>Metodo <strong><em>synchronous beforeEach()<\/em><\/strong>, in cui fixture e components sono istanziati e configurati<\/li><\/ul>\n\n\n\n<p>Una volta definiti questi elementi, si pu\u00f2 procedere alla creazione di un mock service; \u00e8 possibile utilizzare uno strumento messo a disposizione da Jasmine chiamato <strong><em>Spy<\/em><\/strong>. Tramite Spy \u00e8 possibile creare un mock di un oggetto e fare override dei suoi metodi. Nel nostro esempio, andremo a creare un mock service per simulare ci\u00f2 che il CarService dovrebbe fare, ovvero recuperare le informazioni dal server. Di seguito un esempio di come inizializzare un service tramite Spy<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n    \/\/ Create a mock carService object with a mock &#039;getData&#039; method\n    let carsService = jasmine.createSpyObj &lt;CarsService&gt; (&#039;CarsService&#039;, &#x5B;&#039;getData&#039;]);\n    \/\/ Configure the &#039;getData&#039; spy method\n    carsService.getData.and.returnValue(\n        \/\/ return an Observable with some test data\n        of &lt;Car&#x5B;]&gt; (&#x5B;{\n                carId: 1,\n                brand: &#039;Ferrari&#039;,\n                model: &#039;FXXX&#039;,\n                cv: 1050\n            },\n            {\n                carId: 2,\n                brand: &#039;Lamborghini&#039;,\n                model: &#039;Aventador&#039;,\n                cv: 770\n            }\n        ]));\n<\/pre><\/div>\n\n\n<p>Una volta inizializzato il service, ricordarsi di referenziarlo nella sezione providers di TestBed<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nTestBed.configureTestingModule({\n    declarations: &#x5B;CarsComponent],\n    imports: &#x5B;\n        BrowserAnimationsModule\n    ],\n    providers: &#x5B;{\n        provide: CarsService,\n        useValue: carsService\n    }]\n}).compileComponents();\n<\/pre><\/div>\n\n\n<p>Terminata la configurazione di TestBed all\u2019interno del metodo <em>beforeEach<\/em> asincrono, spostarsi nel <em>beforeEach<\/em> sincrono ed aggiungere l\u2019istruzione <em>fixture.detectChanges();<\/em>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\/\/ synchronous beforeEach(): fixtures and components setup\nbeforeEach(() =&gt; {\n    fixture = TestBed.createComponent(CarsComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n});\n<\/pre><\/div>\n\n\n<p>Questo comando solleva il trigger di rilevamento modifiche lato client. In questo modo, avverr\u00e0 correttamente il popolamento delle parti di codice che sono costruite dinamicamente (ad esempio la tabella che mostra i risultati provenienti dal mock service).<br>Ultimata questa fase di configurazione dell\u2019ambiente di test, si possono iniziare a scrivere i test veri e propri. Jasmine prevede il comando <em>it()<\/em> da utilizzare per la definizione del test, mentre il comando <em>expect()<\/em> viene usato per il check vero e proprio che vogliamo effettuare sul test specifico.<br>Un semplice esempio: \u201c<em>voglio testare che la tabella che mostra l&#8217;elenco dei record di cars contenga almeno una riga<\/em>\u201c. Ecco l\u2019esempio di come \u00e8 possibile implementare questa cosa:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nit(&#039;should contain a table with a list of one or more cars&#039;,\n    async (() =&gt; {\n        let table = fixture.nativeElement.querySelector(&#039;table.table-striped&#039;);\n        let tableRows = table.querySelectorAll(&#039;tr&#039;);\n        expect(tableRows.length).toBeGreaterThan(0);\n    }));\n<\/pre><\/div>\n\n\n<p>Come prima cosa assegno una descrizione al test che andr\u00f2 ad eseguire, dopo di che utilizzo la variabile <em>fixture<\/em> per interrogare il DOM. Nel caso specifico, mi voglio assicurare che esista almeno una riga nella tabella. Ultimata la definizione dei test, si \u00e8 pronti per mandarli in esecuzione. <br>Aprire una finestra di prompt dei comandi e posizionarsi nella cartella ClientApp e lanciare il seguente comando <code>ng test<\/code> . L\u2019esecuzione di questa istruzione avvier\u00e0 il Karma test runner, quindi verr\u00e0 aperta una nuova istanza del browser configurato nel file <em>karma.conf.js<\/em> e verranno eseguiti in cascata tutti i test definiti per ogni blocco <em>it()<\/em> dichiarato. Il risultato finale sar\u00e0 un report simile a questo<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"911\" height=\"390\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/karma-res.png\" alt=\"\" class=\"wp-image-2474\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/karma-res.png 911w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/karma-res-300x128.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/karma-res-768x329.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/karma-res-600x257.png 600w\" sizes=\"auto, (max-width: 911px) 100vw, 911px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Protractor<\/h3>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<p>Protractor \u00e8 un test framework di tipo end-to-end, quindi di fatto simula a tutti gli effetti le azioni che l\u2019utente dovrebbe compiere sul browser.<br>Come prima cosa, se il progetto \u00e8 stato creato direttamente da Visual Studio 2019 utilizzando come template di partenza quello della Webapp ASP.NET Core con Angular, automaticamente verr\u00e0 creata una cartella <em>\/ClientApp\/e2e<\/em>, all\u2019interno della quale ci saranno tutta una serie di files di configurazione di base per utilizzare Protractor.<br>Partendo da questa preconfigurazione, elencher\u00f2 passo per passo le operazioni da fare per poter inizializzare ed utilizzare Protractor framework.<br>Si comincer\u00e0 con l\u2019installare il framework con le sue dipendenze; da prompt dei comandi, posizionarsi nella cartella ClientApp ed eseguire i seguenti comandi:<\/p>\n<\/div><\/div>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\nnpm install -g protractor\nwebdriver-manager update\nnpm install jasmine-spec-reporter --save-dev\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\"><li><em>webdriver-manager<\/em> \u00e8 uno strumento di supporto per ottenere un&#8217;istanza di un server Selenium<\/li><li><em>jasmine-spec-reporter<\/em> \u00e8 una libreria che permette di avere in console una visualizzazione pi\u00f9 dettagliata degli esiti dei test<\/li><\/ul>\n\n\n\n<p>A questo punto spostarsi nel file <em>protractor.conf.js<\/em> ed impostare come parametro di baseUrl l\u2019URL del sito che si vuole andare a testare (impostare l\u2019url della webapp che viene creata da Visual Studio).<br>Tra i parametri del file <em>protractor.conf.js<\/em> si evidenzia <em>specs<\/em>; esso conterr\u00e0 l\u2019elenco di tutti i file .ts che contengono i test e che si vorranno mandare in esecuzione. Di default Visual Studio propone il parametro &#8220;.\/src\/**\/*.e2e-spec.ts&#8221; cos\u00ec che tutti i files nella cartella e2e\/src che terminano con <em>.e2e-spec.ts<\/em> saranno eseguiti automaticamente.<br>Fatte queste premesse, spostarsi ora nel file app.e2e-spec.ts per scrivere il primo test. Cos\u00ec come per Karma, anche Protractor si avvale di Jasmine come strumento di scrittura e definizione dei test; quindi il set di test \u00e8 aperto dal comando <em>describe()<\/em> seguito poi dalla definizione dei singoli test tramite i metodi <em>it()<\/em>. Prendiamo questo esempio come riferimento:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\ndescribe(&#039;App&#039;, () =&gt; {  \n  it(&#039;check correct number of rows&#039;, async () =&gt; {\n    await browser.get(&#039;\/&#039;);    \n    await browser.waitForAngular();\n    let elems = element.all(by.tagName(&#039;tbody tr&#039;));    \n    expect(elems.count()).toEqual(1);    \n  });\n});\n<\/pre><\/div>\n\n\n<p>Notare la sequenza delle operazioni programmate:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>L\u2019istanza globale <em>browser<\/em> \u00e8 generata da Protractor ed \u00e8 utilizzata per invocare comandi a livello browser (ad esempio la navigazione tra pagine). Nello specifico di questo esempio, si impone al browser di navigare al baseUrl impostato nel file di configurazione di cui scritto in precedenza<\/li><li><em>waitForAngular()<\/em> attende che Angular abbia terminato tutte le chiamate http ed abbia ultimato il rendering a video dei componenti<\/li><li><em>element.all<\/em> recupera tutti gli elementi HTML che corrispondono al selector passato<\/li><li>Dato che nella base dati che si sta utilizzando contiene un solo record, mi aspetto che ci sia solamente una riga all\u2019interno della tabella<\/li><\/ol>\n\n\n\n<p>A questo punto tutto \u00e8 pronto per poter eseguire il test appena definito. Eseguire questa sequenza di comandi per avviare Protractor nel modo corretto:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Avviare la webapp da Visual Studio<\/li><li>Da prompt dei comandi, posizionarsi nella cartella ClientApp<\/li><li>Eseguire il test appena scritto tramite il comando <code>protractor .\\e2e\\protractor.conf.js<\/code><\/li><\/ol>\n\n\n\n<p>Se tutto \u00e8 stato svolto correttamente, comparir\u00e0 nella console un riepilogo simile al seguente:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"955\" height=\"194\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/protractor.png\" alt=\"\" class=\"wp-image-2481\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/protractor.png 955w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/protractor-300x61.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/protractor-768x156.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/protractor-600x122.png 600w\" sizes=\"auto, (max-width: 955px) 100vw, 955px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">E&#8217; possibile andare in debug dei test scritti con Protractor?<\/h3>\n\n\n\n<p>Certo che \u00e8 possibile, per\u00f2 non lo si pu\u00f2 fare solamente aggiungendo un classico breakpoint <code>debugger<\/code> nel codice, ma sono necessari alcuni passaggi per far si che il comando funzioni:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Avviare la webapp da Visual Studio<\/li><li>In una finestra Powershell, posizionarsi nella cartella ClientApp<\/li><li>Avviare Protractor tramite il comando <code>node --inspect-brk $env:APPDATA\\npm\\node_modules\\protractor\\bin\\protractor .\\e2e\\protractor.conf.js<\/code><\/li><li>Aprire il browser Chrome e digitare nella barra dell\u2019indirizzo <a rel=\"noreferrer noopener\" href=\"\/\/inspect\/#devices\" target=\"_blank\">chrome:\/\/inspect\/#devices<\/a><\/li><li>Attendere che nell\u2019elenco dei \u201cRemote Targets\u201d compaia l\u2019esecuzione lanciata la punto 3<\/li><\/ol>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusioni<\/h1>\n\n\n\n<p>Spesso testare il codice \u00e8 una pratica che viene messa in secondo piano perch\u00e8 la frenesia di avere risultati porta a sacrificare &#8220;ci\u00f2 che non \u00e8 necessario&#8221;. Testare il codice ha un costo effettivamente sia in termini di tempo di analisi che di implementazione, ma \u00e8 un investimento per il futuro. Se i test vengono scritti nel modo corretto e funzionale, pi\u00f9 il software evolver\u00e0 e pi\u00f9 il tempo speso inizialmente torner\u00e0 come tempo risparmiato nel bugfixing. Un altro vantaggio importante che porta lo Unit Testing \u00e8 l&#8217;agevolare l&#8217;integrazione di moduli di software scritti da diversi membri del team di sviluppo.<br>In sostanza: state iniziando un nuovo progetto? Affrontatelo fin da subito con un approccio <strong>BDD<\/strong>,  \u00e8 un investimento che verr\u00e0 sicuramente ripagato nel tempo.<\/p>\n\n\n\n<p><em><strong>L&#8217;esempio presente in questa guida \u00e8 scaricabile a questo <a href=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2020\/06\/UnitTestSample.zip\">link<\/a>.<\/strong><\/em><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Link utili<\/h1>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Getting Started with xUnit.net<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/xunit.net\/docs\/getting-started\/netcore\/cmdline\" target=\"_blank\">https:\/\/xunit.net\/docs\/getting-started\/netcore\/cmdline<\/a><\/li><li><strong>Unit testing in .NET Core and .NET Standard<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-US\/dotnet\/core\/testing\/\" target=\"_blank\">https:\/\/docs.microsoft.com\/en-US\/dotnet\/core\/testing\/<\/a><\/li><li><strong>Unit test controller logic in ASP.NET Core: <\/strong><a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/enus\/aspnet\/core\/mvc\/controllers\/testing\" target=\"_blank\">https:\/\/docs.microsoft.com\/enus\/aspnet\/core\/mvc\/controllers\/testing<\/a><\/li><li><strong>The using statement (C#): <\/strong><a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-US\/dotnet\/csharp\/language-reference\/keywords\/using-statement\" target=\"_blank\">https:\/\/docs.microsoft.com\/en-US\/dotnet\/csharp\/language-reference\/keywords\/using-statement<\/a><\/li><li><strong>xUnit.net \u2013 Using .NET Core with the .NET SDK command line: <\/strong><a href=\"https:\/\/xunit.net\/docs\/getting-started\/netcore\/cmdline\">https:\/\/xunit.net\/docs\/getting-started\/netcore\/cmdline<\/a><\/li><li><strong>Angular \u2013 Testing: <\/strong><a href=\"https:\/\/angular.io\/guide\/testing\">https:\/\/angular.io\/guide\/testing<\/a><\/li><li><strong>Protractor: End-to-end testing for Angular<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/www.protractortest.org\/\" target=\"_blank\">https:\/\/www.protractortest.org\/<\/a><\/li><li><strong>Jasmine: Behavior-Driven JavaScript<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/jasmine.github.io\/\" target=\"_blank\">https:\/\/jasmine.github.io\/<\/a><\/li><li><strong>Karma: Spectacular Test Runner for JavaScript<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/karma-runner.github.io\/latest\/index.html\" target=\"_blank\">https:\/\/karma-runner.github.io\/latest\/index.html<\/a><\/li><li><strong>Angular Testing: ComponentFixture: <\/strong><a rel=\"noreferrer noopener\" href=\"https:\/\/angular.io\/api\/core\/testing\/ComponentFixture\" target=\"_blank\">https:\/\/angular.io\/api\/core\/testing\/ComponentFixture<\/a><\/li><li><strong>Angular References: ngAfterViewInit<\/strong>: <a rel=\"noreferrer noopener\" href=\"https:\/\/ngrefs.com\/latest\/core\/ngafter-view-init\" target=\"_blank\">https:\/\/ngrefs.com\/latest\/core\/ngafter-view-init<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Lo Unit Testing \u00e8 spesso sottovalutato perch\u00e8 ritenuta un&#8217;attivit\u00e0 &#8220;sacrificabile&#8221;, ma se fatto bene si riveler\u00e0 un importante investimento per i propri prodotti.<\/p>\n","protected":false},"author":4,"featured_media":2263,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wds_primary_category":0,"footnotes":""},"categories":[90,109,84,36],"tags":[94,107,111,93,110,108],"class_list":["post-2256","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-angular","category-asp-net-core","category-guide","tag-net-core","tag-angular","tag-c","tag-dotnet-core","tag-tutorial","tag-unit-testing"],"_links":{"self":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/2256","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/comments?post=2256"}],"version-history":[{"count":0,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/2256\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media\/2263"}],"wp:attachment":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media?parent=2256"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/categories?post=2256"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/tags?post=2256"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}