Månedlige Arkiver: Januar 2014

Fattig mann er hurtigbufring i JavaScript

[TL;DR versjon: Bruk informasjonskapsler til å lagre resultatene av asynkrone samtaler; gjengi resultatet av siste asynkrone samtaler umiddelbart og deretter validere dem etter sideinnlastingen.]

Jeg har jobbet på SharePoint intranettområde for en klient som har, blant annet, en stilisert sekundær navigasjon som menyalternativer administreres via en vanlig gamle egendefinert liste.  Tanken er at klienten får Kontrollmenyen "sine" området uten å påvirke eller blir påvirket av global navigasjon satt ut av det.

(Det er noe utrolig samfunnsnedbrytende om å legge til en CEWP som peker til en HTML-fil som laster noen CSS og JS fundamentalt endre nesten alt om nettstedets oppførsel... men det er for en annen post)

Koden for denne pen enkel:

Sår spot her er at hver gang noen treff en av områdets sider, brukerens web-leser er nå for å få elementer fra listen.  Når dev er fullført og testing har vist ting å være stabil og fullføre, Denne samtalen er unødvendig mer enn 99% tiden siden menyen sjelden endres.  Det har også en merkelig UI innvirkning som er vanlig i denne brave new world av hyper-ajaxy nettsteder-siden gjør og deretter gjengir menyen.  Det er nervøs og forstyrrende i min mening.  Og nervøs. Så, hurtigbufring. 

Jeg endret logikken thusly:

  • Se etter en informasjonskapsel i nettleseren som inneholder menyen som jeg sist leste det
    • Hvis funnet, gjøre det umiddelbart.  Ikke vent til siden å slutten lessing.  (Du må kontrollere at HTML er strategisk plassert her, men det er ikke vanskelig å gjøre).
  • Vent til siden å slutten lessing og lage en asynkron behøve laste opp elementer fra en liste med resten eller lists.asmx eller hva
  • Sammenligne hva jeg fikk mot cookie
    • Samsvarer med, stopp
    • Ellers, hjelp jQuery, dynamisk fylle en haug hvis <Li>er i en <UL>
  • Bruk CSS til all formatering
  • Fortjeneste!

Noen av dere skal si, "Hei! Det er ingen reell skjulested vei her siden du leser menyen allikevel hver eneste gang.”  Og du har rett-jeg gi ikke serveren alle slags pause.  Men fordi samtalen er asynkrone og skjer etter siden første HTML nyttelast gjengir fullt, det føles"" mer mottagelig for brukeren.  Menyen gjør ganske mye som siden trekker.  Hvis menyen skjer endringen, brukeren er utsatt for en nervøs re-trekning av menyen, men den ene gangen.

Det er noen måter å gjøre dette hurtigbufring mer effektiv og hjelpe til serveren samtidig:

  • Innlegge en regel at "cookie cache" er gyldig i minst 24 timer eller noen andre tidsramme. Så lenge det er ingen opphøre koke, Bruk informasjonskapselen menyen bilde og aldri truffet serveren.

Vel... det er alt som kommer til hjernen akkurat nå :). 

Hvis noen har noen smarte ideer her ville jeg elske å kjenne dem..

Og til slutt-denne teknikken kan brukes til andre ting.  Denne klienten siden har en rekke data-drevet ting på forskjellige sider, mange av dem endre relativt sjelden (som en gang i uken eller en gang i måneden).  Hvis du målrette bestemte områder av funksjonalitet, Du kan gi en mer responsiv UI ved å trekke innhold fra lokale cookie store og gjengivelse umiddelbart.  Det føles raskere til brukeren selv om du ikke lagrer serveren noen sykluser.  Du kan Lagre server sykluser med å beslutter på noen betingelser og utløsere å oppheve denne lokale cookie cache.  Det er alle situasjonsforståelse og fancy ting og virkelig det morsomste :). 

</slutten>

undefinedAbonner på bloggen min.

Følg meg på Twitter på http://www.twitter.com/pagalvin

hvordan: Konfigurere Enhetstest og testen dekning med QUnit.js og Blanket.js For et kontor 365 SharePoint App

Intro

Jeg har vært å utforske enhetstesting og teste dekning for JavaScript som jeg jobber på en ny SharePoint-app for SharePoint online i Office 365 Suite.  Banene som selvskreven forskning ledet meg til å Qunit.js og etter at, til Blanket.js.

QUnit la meg definere enhet tester og gruppere dem i moduler.  En modul er bare en enkel måte å organisere relaterte tester. (Jeg er ikke sikker jeg bruker det som den skal, men det fungerer for meg så langt med de små tester jeg hittil har definert).

Blanket.js integreres med Qunit og det viser meg de faktiske linjene med JavaScript som var- og enda viktigere-var ikke faktisk utført i løpet av tester.  Dette er "dekning"-linjer som kjøres som er dekket av testen, mens andre ikke.

Mellom definere god test sakene og vise dekning, Vi kan redusere risikoen for at våre koden har gjemt defekter.  Gode tider.

Qunit

Antar du har Visual Studio prosjekt satt, Start ved å laste ned JavaScript pakken fra http://qunitjs.com.  Legg til JavaScript og tilsvarende CSS i løsningen.  Min ser slik ut:

image

Figur 1

Som du kan se, Jeg brukte 1.13.0 på tiden skrev jeg dette blogginnlegget. Ikke glem å laste ned og legge CSS-filen.

Det ut av veien, neste skritt er å lage en slags test seletøy og referanse Qunit biter.  Jeg tester en haug av funksjoner i en skriptfil kalt "QuizUtil.js" så jeg laget en HTML-side som kalles "QuizUtil_test.html" som vist:

image Figur 2

Her er koden:

<!DOCTYPE HTML>
<HTML xmlns= "http://www.w3.org/ 1999/xhtml">
<hodet>
    <tittel>QuizUtil test med Qunit</tittel>
    <kobling rel= "stylesheet" href="../CSS/qunit-1.13.0.CSS" />
    <skriptet type= text/javascript"" src="QuizUtil.js" data-cover></skriptet>
    <script type ="text/javascript" src ="qunit-1.13.0.js"></skriptet>
    <script type ="text/javascript" src ="blanket.min.js"></skriptet>

    <skriptet>
        modul("getIDFromLookup");
        test("QuizUtil getIDFromLookupField", funksjonen () {
            var goodValue = "1;#Paul Galvin";

            lik(getIDFromLookupField(goodValue) + 1, 2), "ID av [" + goodValue + "] + 1 bør være 2";
            lik(getIDFromLookupField(Udefinert), Udefinert, "Udefinert inndataargumentet skal returnere udefinert resultatet.");
            lik(getIDFromLookupField(""), Udefinert, "Tom inndataargumentet skal returnere en udefinert verdi.");
            lik(getIDFromLookupField("gobbledigood3-thq;dkvn ada;skfja sdjfbvubvqrubqer0873407t534piutheqw;vn"), Udefinert,"Bør alltid returnere en kabriolet resultatet til et heltall");
            lik(getIDFromLookupField("2;#en annen person"), "2", "Kontrollere [2;#en annen person].");
            lik(getIDFromLookupField("9834524;#lang verdi"), "9834524", "Stor verdi test.");
            notEqual(getIDFromLookupField("5;#noen", 6), 6, "Testing en notEqual (5 er ikke lik 6 for dette eksemplet: [5;#noen]");

        });

        modul("htmlEscape");
        test("QuizUtil htmlEscape()", funksjonen () {
            lik(htmlEscape("<"), "&lt;", «Escaping et mindre enn-operator ('<')");
            lik(htmlEscape("<div class =  "someclass">Tekst</div>"), "&lt;div class =&quot;SomeClass&quot;&gt;Tekst&lt;/div&gt;", "Mer komplekse teststrengen.");
        });

        modul("getDateAsCaml");
        test("QuizUtil getDateAsCaml()", funksjonen () {
            lik(getDateAsCaml(nye Dato("12/31/2013")), "2013-12-31T:00:00:00", "Testing hardkodet dato: [12/31/2013]");
            lik(getDateAsCaml(nye Dato("01/05/2014")), "2014-01-05T:00:00:00", "Testing hardkodet dato: [01/05/2014]");
            lik(getDateAsCaml(nye Dato("01/31/2014")), "2014-01-31T:00:00:00", "Testing hardkodet dato: [01/31/2014]");
            lik(getTodayAsCaml(), getDateAsCaml(nye Dato()), "getTodayAsCaml() bør tilsvare getDateAsCaml(ny dato())");
            lik(getDateAsCaml("tull verdi"), Udefinert, "Prøv å få datoen for en tull verdi.");
            lik(getDateAsCaml(Udefinert), Udefinert, "Prøv å få datoen for den [Udefinert] datoen.");
        });

        modul("getParameterByName");
        test("QuizUtil getParameterByName (fra søkestrengen)", funksjonen () {
            lik(getParameterByName(Udefinert), Udefinert, "Prøv å få udefinert parameter skal returnere udefinert.");
            lik(getParameterByName("finnes ikke"), Udefinert, "Prøv å få parameterverdi når vi vet parameteren ikke finnes.");

        });

        modul("Cookies");
        test("QuizUtil ulike cookie funksjoner.", funksjonen () {
            lik(setCookie("test", "1", -1), getCookieValue("test"), "Få en informasjonskapsel jeg skal fungere.");
            lik(setCookie("anycookie", "1", -1), sann, "Angi en gyldig matlaging skal returnere 'true'.");
            lik(setCookie("crazy informasjonskapselnavn !@#$%"%\^&*(()?/><.,", "1", -1), sann, "Angi en dårlig informasjonskapselnavn skal returnere false'.");
            lik(setCookie(Udefinert, "1", -1), Udefinert, "Passerer udefinert som informasjonskapselen.");
            lik(getCookieValue("finnes ikke"), "", "Cookien ikke finnes test.");
        });

    </skriptet>
</hodet>
<kroppen>
    <div ID= "qunit"></div>
    <div ID= "qunit-lampen"></div>

</kroppen>
</HTML>

Det er flere ting skjer her:

  1. Referanse kode (QuizUtil.js)
  2. Henviser Qunity.js
  3. Definere noen moduler (getIDFromLookup, Informasjonskapsler, og andre)
  4. Plassere en <div> Hvis ID er "qunit".

Deretter, Jeg trekke bare opp denne siden og du får noe som dette:

image

Figur 3

Hvis du ser øverst, har du noen alternativer, to av dem er interessant:

  • Skjul bestått tester: Ganske åpenbart.  Kan hjelpe deg bare se problemområder og ikke en masse rot.
  • Modul: (rullegardinmenyen): Dette vil filtrere testene til bare de gruppene av tester du vil.

Som for prøver selv-noen kommentarer:

  • Det går uten å si at du trenger å skrive koden slik at det er testbare i første omgang.  Ved hjelp av verktøyet kan bidra til å håndheve den disiplinen. For eksempel, Jeg hadde en funksjon kalt "getTodayAsCaml()”.  Dette er ikke etterprøvbar siden det tar ingen inndataargumentet og teste det for likestilling, Vi må kontinuerlig oppdatere test koden for å gjenspeile gjeldende dato.  Jeg refactored det ved å legge til en data-inndataparameteren deretter passerer gjeldende dato når jeg vil dagens dato i CAML-format.
  • Qunit rammen dokumenter sine egne tester og det synes ganske robust.  Det kan gjøre enkle ting som tester for likestilling og har også støtte for ajax-stil samtaler (både "ekte" eller spottet benytter din favoritt spotter).
  • Gå gjennom prosessen tvinger deg til å tenke gjennom kanten-hva skjer med "udefinert" eller null er sendt til en funksjon.  Det gjør det døde enkelt å teste disse scenariene ut.  Gode ting.

Dekning med Blanket.js

Blanket.js utfyller Qunit ved å spore faktiske kodelinjene som utføres i løpet av din tester.  Det integreres rett i Qunit så selv om det er en helt egen app, Det spiller pent-det egentlig ser ut som det er en sømløs app.

Dette er blanket.js i aksjon:

image Figur 4

image

Figur 5

(Faktisk må du klikke på avkrysningsruten «Aktiver dekning» øverst [se figur 3] aktivere dette.)

De uthevede linjene i figur 5 ikke er utført av noen av mine tester, så jeg trenger å tenke ut en test som forårsaker dem til å kjøre hvis jeg vil full dekning.

Få blanket.js fungerer på følgende måte:

  1. Laste det ned fra http://blanketjs.org/.
  2. Legge det til i prosjektet
  3. Oppdatere testsiden sele (QuizUtil_test.html i mitt tilfelle) slik:
    1. Referanse koden
    2. Dekorere din <skriptet> referanse som dette:
    <skriptet type= text/javascript"" src="QuizUtil.js" data-cover></skriptet>

Blanket.js plukker opp attributtet "data-cover" og gjør sin magi.  Det kroker i Qunit, oppdaterer Grensesnittet for å legge til alternativet "Aktiver dekning" og voila!

Sammendrag (TL; DR)

Bruk Qunit til å skrive din test tilfeller.

  • Dataoverføre den
  • Legge det til i prosjektet
  • Skrive en test seletøy for
  • Opprette tester
    • Refactor noen av din koden skal være testbare
    • Vær kreativ!  Tenk på gal, umulig scenarier og teste dem allikevel.

Bruk blanket.js å sikre dekning

  • Kontroller at Qunit fungerer
  • Last ned blanket.js og legger den til prosjektet
  • Legge det til sele testsiden:
    • Legg til en referanse blanket.js
    • Legge til en "data-cover" attributt til din <skriptet> Tag
  • Kjøre Qunit tester.

Jeg har aldri gjorde noe av dette før og hadde noen enkle ting i en håndfull timer. 

Happy testing!

</slutten>

undefinedAbonner på bloggen min.

Følg meg på Twitter på http://www.twitter.com/pagalvin