Tips for å unngå sprø brukergrensesnitt

I den siste artikkelen snakket jeg om noen ideer og mønstre, som Page Object-mønsteret, som bidrar til å skrive vedlikeholdsgrensesnitt. I denne artikkelen skal vi diskutere noen avanserte emner som kan hjelpe deg med å skrive mer robuste tester og feilsøke dem når de feiler:

  • Vi diskuterer hvorfor å legge til faste forsinkelser i UI-tester er en dårlig ide, og hvordan du kan bli kvitt dem.
  • Nettleserautomatiseringsrammer målrettes UI-elementer ved hjelp av selektorer, og det er svært viktig å bruke gode selektorer for å unngå sprø tester. Så jeg gir deg råd om valg av riktige selektorer og målrettingselementer direkte når det er mulig.
  • UI-tester svikter oftere enn andre typer tester, så hvordan kan vi feilsøke en ødelagt UI-test og finne ut hva som forårsaket feilen? I denne delen viser jeg deg hvordan du kan fange et skjermdump og HTML-kilde til siden når en brukergrensesnitt test mislykkes, slik at du kan undersøke det lettere.

Jeg skal bruke Selen til nettleserautomatiseringsemner som diskuteres i denne artikkelen.

I likhet med forrige artikkel gjelder konseptene og løsningene som diskuteres i denne artikkelen, uavhengig av hvilket språk og brukergrensesnitt du bruker. Før du går videre, vennligst les den forrige artikkelen som jeg skal referere til den og dens prøvekode noen ganger. Ikke bekymre deg; Jeg venter her.


Ikke legg forsinkelser til testene dine

legge Thread.Sleep (eller generelt forsinkelser) føles som en uunngåelig hack når det gjelder UI-testing. Du har en test som mislykkes periodisk og etter noen undersøkelser sporer du det tilbake til sporadiske forsinkelser i svaret. For eksempel navigerer du til en side og ser eller hevder for noe før siden er fullastet og nettleserautomatiseringsrammen kaster et unntak som indikerer at elementet ikke eksisterer. Mange ting kan bidra til denne forsinkelsen. For eksempel:

  • Webserveren, databasen og / eller nettverket er overbelastet og opptatt med andre forespørsler.
  • Siden under test er langsom fordi den laster mye data og / eller spørrer mange bord.
  • Du venter på at noen hendelser skal skje på siden som tar tid.

Eller en blanding av disse og andre problemer.

La oss si at du har en side som normalt tar mindre enn et sekund å laste inn, men prøvene som rammer den svikter nå og da på grunn av sporadisk lag som svar. Du har noen muligheter:

  • Du legger ikke til en forsinkelse: i dette tilfellet testene som treffer den siden noen ganger kommer til å mislykkes, noe som reduserer tilliten til testene.
  • Du legger til en sekunders forsinkelse til testene som rammer den siden: i dette tilfellet alle av disse testene er alltid Skal ta et sekund lenger, selv når siden laster raskt, men selv da er testene ikke garantert å passere da siden kanskje tar lengre tid enn et sekund å laste.
  • Du kan bestemme deg for å legge til noen sekunders forsinkelse: dette sørger for at siden alltid er lastet inn, men nå tar brukergrensesnittene lengre og lengre tid.

Du ser, det er ingen vinnende med vilkårlig forsinkelse: du får enten en treg eller en sprø testpakke. Her skal jeg vise deg hvordan du unngår å sette inn faste forsinkelser i testene dine. Vi skal diskutere to typer forsinkelser som bør dekke stort sett alle tilfeller du må håndtere: legge til en global forsinkelse og vente på at noe skal skje.

Legge til en global forsinkelse

Hvis alle sidene dine tar omtrent samme tid for å laste, noe som er lengre enn forventet, vil de fleste tester svikte på grunn av en tidlig respons. I slike tilfeller kan du bruke Implisitte ventetider:

En implisitt vente er å fortelle WebDriver å avgjøre DOM for en viss tid når du prøver å finne et element eller elementer hvis de ikke er umiddelbart tilgjengelige. Standardinnstillingen er 0. Når den er satt, er den implisitte ventetiden satt for livet til WebDriver-objektet.

Slik angir du en implisitt ventetid:

WebDriver driver = ny FirefoxDriver (); .. Driver.Manage () Tidsavbrudd () ImplicitlyWait (TimeSpan.FromSeconds (5));

På denne måten forteller du at Selen skal vente i opptil 5 sekunder når den prøver å finne et element eller samhandle med siden. Så nå kan du skrive:

driver.Url = "http: // somedomain / url_that_delays_loading"; IWebElement myDynamicElement = driver.FindElement (By.Id ("someDynamicElement"));

i stedet for:

driver.Url = "http: // somedomain / url_that_delays_loading"; Thread.Sleep (5000); IWebElement myDynamicElement = driver.FindElement (By.Id ("someDynamicElement"));

Fordelen med denne tilnærmingen er det FindElement kommer tilbake så snart det finner elementet og venter ikke på hele 5 sekunder når elementet er tilgjengelig før.

Når implisitt vent er satt på din WebDriver eksempel det gjelder alle handlinger på sjåføren; slik at du kan bli kvitt mange Thread.Sleeps i koden din.

5 sekunder er en vent jeg laget for denne artikkelen - du bør finne den optimale implisitte vente på søknaden din, og du bør gjøre denne ventetiden så kort som mulig. Fra API-dokumentasjonene:

Øke implisitt ventetidstiden skal brukes dømmende, da det vil ha en ugunstig effekt på testkjøretiden, spesielt når den brukes med langsommere plasseringsstrategier som XPath.

Selv om du ikke bruker XPath, bruker lang, implisitt venter, bremser testene dine, spesielt når noen tester virkelig svikter, fordi nettdriveren skal vente lenge før det går ut og kaster et unntak.

Venter på eksakte hendelser / endringer

Bruk av implisitt vent er en fin måte å kvitte seg med mange hardkodede forsinkelser i koden din. men du vil fortsatt finne deg selv i en situasjon der du må legge til noen faste forsinkelser i koden din fordi du venter på at noe skal skje: en side er tregere enn alle andre sider, og du må vente lenger, du er venter på en AJAX-samtale for å fullføre eller for et element som skal vises på eller forsvinne fra siden etc. Dette er hvor du trenger eksplisitte venter.

Eksplisitt ventetid

Så du har satt den implisitte vente til 5 sekunder, og den fungerer for mange av testene dine; men det er fortsatt noen sider som noen ganger tar mer enn 5 sekunder å laste og resultere i sviktende tester.

Som en side notat bør du undersøke hvorfor en side tar så lenge først, før du prøver å fikse den ødelagte testen ved å la den vente lenger. Det kan være et ytelsesproblem på siden som fører til den røde testen. I så fall bør du fikse siden, ikke testen.

Ved en sakte side kan du erstatte faste forsinkelser med eksplisitt ventetid:

Et eksplisitt venter er kode du definerer for å vente på at en viss tilstand skal oppstå før du fortsetter videre i koden.

Du kan søke eksplisitt venter ved å bruke WebDriverWait klasse. WebDriverWait bor i WebDriver.Support montering og kan installeres ved hjelp av Selenium.Support nuget:

///  /// Gir muligheten til å vente på en vilkårlig tilstand under testkjøring. ///  offentlig klasse WebDriverWait: DefaultWait ///  /// Initialiserer en ny forekomst av  klasse. ///  /// WebDriver-forekom vant til å vente.Timeout-verdien indikerer hvor lenge å vente på tilstanden. offentlig WebDriverWait (IWebDriver driver, TimeSpan timeout); ///  /// Initialiserer en ny forekomst av  klasse. ///  /// Et objekt som implementerer  grensesnitt som brukes til å bestemme når tiden er gått.WebDriver-forekom vant til å vente.Timeout-verdien indikerer hvor lenge å vente på tilstanden.EN  verdi som indikerer hvor ofte å sjekke at tilstanden er sant. offentlig WebDriverWait (IClock klokke, IWebDriver driver, TimeSpan timeout, TimeSpan sleepInterval); 

Her er et eksempel på hvordan du kan bruke WebDriverWait i testene dine:

driver.Url = "http: // somedomain / url_that_takes_a_long_time_to_load"; WebDriverWait wait = ny WebDriverWait (driver, TimeSpan.FromSeconds (10)); var myDynamicElement = wait.Utiltil (d => d.FindElement (By.Id ("someElement")));

Vi forteller Selen at vi vil at den skal vente på denne siden / elementet i opptil 10 sekunder.

Du vil sannsynligvis ha noen få sider som tar lengre tid enn standard implisitt vente, og det er ikke en god kodingspraksis å fortsette å gjenta denne koden overalt. Tross alt Testkode er kode. Du kan i stedet trekke dette inn i en metode og bruke den fra testene dine:

offentlig IWebElement FindElementWithWait (Ved, int secondsToWait = 10) var wait = ny WebDriverWait (WebDriver, TimeSpan.FromSeconds (secondsToWait)); vent igjen.tiltil (d => d.FindElement (ved)); 

Deretter kan du bruke denne metoden som:

var slowPage = ny SlowPage ("http: // somedomain / url_that_takes_a_long_time_to_load"); var element = slowPage.FindElementWithWait (By.Id ("someElement"));

Dette er et godt eksempel å vise hvordan metoden kan se ut og hvordan den kan brukes. Ideelt sett vil du flytte alle sidevirkninger til sideobjektene dine.

Alternativt eksplisitt venteteksempel

La oss se et annet eksempel på en eksplisitt ventetid. Noen ganger er siden fulllastet, men elementet er ikke der ennå fordi det senere lastes inn som resultat av en AJAX-forespørsel. Kanskje det ikke er et element du venter på, men bare vil vente på en AJAX-interaksjon for å fullføre før du kan gjøre et påstand, si i databasen. Igjen er det her de fleste utviklere bruker Thread.Sleep for å sørge for at for eksempel at AJAX-anropet er gjort, og posten er nå i databasen før de går videre til neste linje av testen. Dette kan lett utbedres ved hjelp av JavaScript-kjøring!

De fleste nettleserautomatiseringsrammer lar deg kjøre JavaScript på den aktive økten, og Selen er ikke noe unntak. I Selen er det et grensesnitt kalt IJavaScriptExecutor med to metoder:

///  /// Definerer grensesnittet der brukeren kan utføre JavaScript. ///  offentlig grensesnitt IJavaScriptExecutor ///  /// Utfører JavaScript i sammenheng med den valgte rammen eller vinduet. ///  /// JavaScript-koden som skal utføres. ///  /// Verdien returnert av skriptet. ///  objekt ExecuteScript (streng script, params objekt [] args); ///  /// Utfører JavaScript asynkront i sammenheng med den valgte rammen eller vinduet. ///  /// JavaScript-koden som skal utføres. ///  /// Verdien returnert av skriptet. ///  objekt ExecuteAsyncScript (streng script, params objekt [] args); 

Dette grensesnittet er implementert av RemoteWebDriver som er grunnklassen for alle nettdriverimplementeringer. Så på web driveren din kan du ringe ExecuteScript å kjøre et JavaScript-skript. Her er en metode du kan bruke til å vente på alle AJAX-anrop til slutt (forutsatt at du bruker jQuery):

// Dette antas å leve i en klasse som har tilgang til den aktive "WebDriver" -fasen via "WebDriver" -felt / -eiendom. offentlig tomrom WaitForAjax (int secondsToWait = 10) var wait = ny WebDriverWait (WebDriver, TimeSpan.FromSeconds (secondsToWait)); vent.tiltil (d => (bool) ((IJavaScriptExecutor) d) .ExecuteScript ("return jQuery.active == 0")); 

Kombiner ExecuteScript med WebDriverWait og du kan bli kvitt Thread.Sleep lagt til for AJAX-anrop.

jQuery.active returnerer antall aktive AJAX-anrop initiert av jQuery; så når det er null, er det ingen AJAX-anrop pågår. Denne metoden fungerer åpenbart bare hvis alle AJAX-forespørsler er initiert av jQuery. Hvis du bruker andre JavaScript-biblioteker for AJAX-kommunikasjon, bør du konsultere API-dokumentasjonene for en tilsvarende metode eller holde orden på AJAX-samtaler selv.

ExpectedCondition

Med eksplisitt vente kan du stille en betingelse og vente til den er oppfylt eller for timeout å utløpe. Vi så hvordan vi kunne se etter AJAX-anrop for å fullføre - et annet eksempel er å sjekke om et element er synlig. På samme måte som AJAX-sjekken, kan du skrive en betingelse som kontrollerer synligheten til et element; men det er en enklere løsning for det som kalles ExpectedCondition.

Fra Selen dokumentasjon:

Det er noen vanlige forhold som ofte kommer over når du automatiserer nettlesere.

Hvis du bruker Java har du lykke fordi ExpectedCondition Klassen i Java er ganske omfattende og har mange praktiske metoder. Du finner dokumentasjonen her.

.Nettoutviklere er ikke så heldige. Det er fortsatt en ExpectedConditions klasse i WebDriver.Support montering (dokumentert her), men det er veldig minimal:

offentlig forseglet klasse ExpectedConditions ///  /// En forventning for å sjekke tittelen på en side. ///  /// Den forventede tittelen, som må være en nøyaktig kamp. ///  ///  når tittelen matcher; ellers, . ///  offentlig statisk Func TitleIs (strengtittel); ///  /// En forventning om å kontrollere at tittelen på en side inneholder en sosialfølsom substring. ///  /// Titelfragmentet forventes. ///  ///  når tittelen matcher; ellers, . ///  offentlig statisk Func TitleContains (strengtittel); ///  /// En forventning om å kontrollere at et element er til stede på DOM av en ///-side. Dette betyr ikke nødvendigvis at elementet er synlig. ///  /// Locatoren brukte å finne elementet. ///  /// The  når den er plassert. ///  offentlig statisk Func ElementExists (By locator); ///  /// En forventning om å kontrollere at et element er til stede på DOM av en side /// og synlig. Sikt betyr at elementet ikke bare vises, men /// har også en høyde og bredde som er større enn 0. ///  /// Locatoren brukte å finne elementet. ///  /// The  når den er plassert og synlig. ///  offentlig statisk Func ElementIsVisible (By locator); 

Du kan bruke denne klassen i kombinasjon med WebDriverWait:

var vent = ny WebDriverWait (driver, TimeSpan.FromSeconds (3)) var element = vent.tiltil (ExpectedConditions.ElementExists (By.Id ("foo")));

Som du kan se fra klassen signaturen ovenfor kan du sjekke om tittelen eller deler av den og for eksistens og synlighet av elementer ved hjelp av ExpectedCondition. Ut av boksen støtte i. Net kan være veldig minimal; men denne klassen er bare en wrapper rundt noen enkle forhold. Du kan like enkelt implementere andre vanlige forhold i en klasse og bruke den med WebDriverWait fra testskriptene dine.

FluentWait

En annen perle bare for Java-utviklere er FluentWait. Fra dokumentasjonssiden, FluentWait er

En implementering av Wait-grensesnittet som kan ha tidsavbrudd og pollingintervall konfigureres i fly. Hver FluentWait-forekomst definerer maksimal tid for å vente på en tilstand, samt frekvensen for å kontrollere tilstanden. Videre kan brukeren konfigurere ventetiden til å ignorere bestemte typer unntak mens du venter, for eksempel NoSuchElementExceptions når du søker etter et element på siden.

I det følgende eksemplet prøver vi å finne et element med ID foo På siden avstemmes hvert femte sekund i opptil 30 sekunder:

// Venter 30 sekunder for at et element skal være til stede på siden, og sjekker // for nærværet hvert femte sekund. Vente vent = ny fluentwait(driver) .withTimeout (30, SECONDS) .pollingEvery (5, SECONDS) .ignoring (NoSuchElementException.class); WebElement foo = wait.until (ny funksjon() offentlig WebElement gjelder (WebDriver driver) return driver.findElement (By.id ("foo")); );

Det er to gode ting om FluentWait: For det første lar du deg angi avstemningsintervallet som kan forbedre testytelsen din, og for det andre tillater du å ignorere unntakene du ikke er interessert i.

FluentWait er ganske kjempebra, og det ville være kult hvis et tilsvarende eksisterte i. Net too. Når det er sagt, er det ikke så vanskelig å implementere det med WebDriverWait.


Velg de riktige velgene

Du har dine sidobjekter på plass, har en fin DRY-vedlikeholds-testkode, og unngår også faste forsinkelser i testene dine. men testene dine mislykkes fortsatt!

Brukergrensesnittet er vanligvis den mest forandrede delen av en typisk applikasjon: Noen ganger beveger du elementer rundt på en side for å endre utformingen av siden og noen ganger endringer i sidestrukturen basert på krav. Disse endringene på sidelayout og design kan føre til mange ødelagte tester hvis du ikke velger selektorer dine klokt.

Ikke bruk fuzzy selectors og ikke stole på strukturen på siden din.

Mange ganger har jeg blitt spurt om det er greit å legge til en ID på elementene på siden bare for testing, og svaret er en rungende ja. For å gjøre testen vår kodenheten, gjør vi mange endringer i det som å legge til grensesnitt og bruke Dependency Injection. Testkode er kode. Gjør det som trengs for å støtte testene dine.

La oss si at vi har en side med følgende liste:

  • Det beste av menn på jobb
  • For de som skal rocke, hilser vi deg
  • La det bli rock

I et av mine tester vil jeg klikke på albumet "Let There Be Rock". Jeg ville spørre om problemer hvis jeg brukte følgende valg:

By.XPath ( "// ul [@ id = 'album-liste'] / li [3] / a")

Når det er mulig bør du legge til ID til elementer og målrette dem direkte og uten å stole på de omgivende elementene. Så jeg skal gjøre en liten endring til listen:

  • Det beste av menn på jobb
  • For de som skal rocke, hilser vi deg
  • La det bli rock

jeg har lagt til id attributter til ankre basert på det unike albumets ID slik at vi kan målrette en link direkte uten å måtte gå gjennom ul og li elementer. Så nå kan jeg erstatte den sprøvelgeren med By.Id ( "album-35") som er garantert å fungere så lenge albumet er på siden, som forresten er en god påstand også. For å opprette den valgte jeg åpenbart må ha tilgang til album-ID fra testkoden.

Det er ikke alltid mulig å legge til unike ids for elementer selv, som rader i et rutenett eller elementer i en liste. I slike tilfeller kan du bruke CSS-klasser og HTML-dataattributter til å feste sporbare egenskaper til elementene dine for lettere valg. Hvis du for eksempel hadde to lister over album på siden din, en som følge av brukersøk og en annen for foreslåtte album basert på brukerens tidligere kjøp, kan du skille dem fra ved hjelp av en CSS-klasse på ul element, selv om denne klassen ikke er brukt til å utforme listen:

Hvis du foretrekker å ikke ha ubrukte CSS-klasser, kan du i stedet bruke HTML-dataattributter og endre lister til:

og:


Debugging UI Tests

En av hovedårsakene til at UI-tester mislykkes, er at et element eller en tekst ikke finnes på siden. Noen ganger skjer dette fordi du lander på en feil side på grunn av navigasjonsfeil, eller endringer i sidevisninger på nettstedet ditt, eller valideringsfeil. Andre ganger kan det skyldes en manglende side eller en serverfeil.

Uansett hva som forårsaker feilen og om du får dette på CI-serverloggen eller i din skrivebords-testkonsoll, a NoSuchElementException (eller lignende) er ikke ganske nyttig for å finne ut hva som gikk galt, er det? Så når testen mislykkes, er den eneste måten å feilsøke feilen på å kjøre den igjen og se den som den mislykkes. Det er noen triks som potensielt kan spare deg for å gjenopprette dine langsomme brukergrensesnitt for feilsøking. En løsning på dette er å fange et skjermbilde når en test mislykkes, slik at vi senere kan referere til det.

Det er et grensesnitt i Selen som kalles ITakesScreenshot:

///  /// Definerer grensesnittet som brukes til å ta skjermbilder av skjermen. ///  offentlig grensesnitt ITakesScreenshot ///  /// Gets a  objekt som representerer bildet av siden på skjermen. ///  /// ///  /// A  objekt som inneholder bildet. ///  Skjermbilde GetScreenshot (); 

Dette grensesnittet er implementert av webdriverklasser og kan brukes slik:

var skjermbilde = driver.GetScreenshot (); screenshot.SaveAsFile ("", ImageFormat.Png);

På denne måten når en test mislykkes fordi du er på feil side, kan du raskt finne ut det ved å sjekke det fangede skjermbildet.

Selv fange skjermbilder er ikke alltid nok skjønt. For eksempel kan du se elementet du forventer på siden, men testen mislykkes fortsatt og sier at den ikke finner den, kanskje på grunn av feil väljare som fører til mislykket elementoppslag. Så i stedet for (eller å utfylle) skjermbildet, kan du også fange sidekilden som html. Det er en kildetekst eiendom på IWebDriver grensesnitt (som implementeres av alle webdriverne):

///  /// Går kilden til siden sist lastet av nettleseren. ///  ///  /// Hvis siden har blitt endret etter lasting (for eksempel ved JavaScript) /// er det ingen garanti for at den returnerte teksten er den på den endrede siden. /// Vennligst se dokumentasjonen til den aktuelle driveren som brukes til /// avgjøre om den returnerte teksten gjenspeiler den nåværende tilstanden til siden /// eller teksten sist sendt av webserveren. Sidekilden som returneres er en /// representasjon av den underliggende DOM: Forvent ikke at den skal formateres / / eller rømmet på samme måte som svaret som sendes fra webserveren. ///  streng PageSource get; 

Akkurat som vi gjorde med ITakesScreenshot du kan implementere en metode som tar tak i sidekilden og vedvarer den til en fil for senere inspeksjon:

File.WriteAllText ("", driver.PageSource);

Du ønsker egentlig ikke å fange skjermbilder og sidekilder til alle sidene du besøker, og for beståttester; ellers må du gå gjennom tusenvis av dem når noe faktisk går galt. I stedet bør du bare fange dem når en test mislykkes eller ellers når du trenger mer informasjon for feilsøking. For å unngå forurensning av koden med for mange prøvefeltblokker og for å unngå kodeduplikasjoner, bør du sette alle elementoppslagene dine og påstandene i en klasse og pakke dem med prøvefange og deretter ta skjermbildet og / eller sidekilden i fangstblokken . Her er litt kode du kan bruke til å utføre handlinger mot et element:

Offentlig tomgang Utfør (Ved, Handling handling) prøv var element = WebDriver.FindElement (ved); virkning (element);  fangst var capturer = ny Capturer (WebDriver); capturer.CaptureScreenshot (); capturer.CapturePageSource (); kaste; 

De fanger klassen kan implementeres som:

offentlig klasse Capturer offentlig statisk streng OutputFolder = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, "FailedTests"); privat leselig RemoteWebDriver _webDriver; offentlig Capturer (RemoteWebDriver webDriver) _webDriver = webDriver;  offentlig tomgang CaptureScreenshot (strengfilnavn = null) var kamera = (ITakesScreenshot) _webDriver; var screenshot = camera.GetScreenshot (); var screenShotPath = GetOutputFilePath (filnavn, "png"); screenshot.SaveAsFile (screenShotPath, ImageFormat.Png);  offentlig tomgang CapturePageSource (strengfilnavn = null) var filePath = GetOutputFilePath (filnavn, "html"); File.WriteAllText (filePath, _webDriver.PageSource);  privat streng GetOutputFilePath (strengfilnavn, strengfilutvidelse) if (! Directory.Exists (OutputFolder)) Directory.CreateDirectory (OutputFolder); var windowTitle = _webDriver.Title; fileName = filnavn ?? string.Format ("0 1. 2", windowTitle, DateTime.Now.ToFileTime (), fileExtension) .Replace (':', '.'); var outputPath = Path.Combine (OutputFolder, filnavn); var pathChars = Path.GetInvalidPathChars (); var stringBuilder = ny StringBuilder (outputPath); foreach (var element i pathChars) stringBuilder.Replace (item, '.'); var screenShotPath = stringBuilder.ToString (); return screenShotPath; 

Denne implementeringen fortsetter skjermbildet og HTML-kilden i en mappe som heter FailedTests ved siden av testene, men du kan endre den hvis du vil ha forskjellig oppførsel.

Selv om jeg bare viste metoder som er spesifikke for Selen, finnes tilsvarende APIer i alle automatiseringsrammer jeg kjenner og kan lett brukes.


Konklusjon

I denne artikkelen snakket vi om noen UI-testtips og triks. Vi diskuterte hvordan du kan unngå en sprø og langsom UI-testpakke ved å unngå faste forsinkelser i testene dine. Vi diskuterte da hvordan du unngår sprø selectors og tester ved å velge selectors klokt og også hvordan du feilsøker brukergrensesnittene dine når de feiler.

Mesteparten av koden vist i denne artikkelen finnes i MvcMusicStore-prøveregisteret som vi så i den siste artikkelen. Det er også verdt å merke seg at mye kode i MvcMusicStore ble lånt fra Seleno kodebase, så hvis du vil se mange flotte triks, vil du kanskje sjekke Seleno ut. Ansvarsfraskrivelse: Jeg er medstifter av TestStack organisasjon og en bidragsyter på Seleno.

Jeg håper det vi har diskutert i denne artikkelen, hjelper deg i oppgavespørsmålene dine.