Månedligt arkiv: Januar 2014

Fattig mand Caching i JavaScript

[TL;DR version: Brug cookies til at gemme resultaterne af async opkald; gengive resultaterne af tidligere async opkald straks og derefter validere dem efter side-belastning.]

Jeg har arbejdet på SharePoint intranet site for en klient at funktioner, blandt andet, en stiliseret sekundær navigation hvis menuindstillinger styres via en almindelig gamle brugerdefineret liste.  Ideen er at klienten får lov til at kontrollere menuen "deres" websted uden at påvirke eller påvirkes af den globale navigation sat ud af det.

(der er noget utrolig undergravende om tilføjelse af en CEWP, der peger på en HTML-fil for at indlæse nogle CSS og JS fundamentalt ændre næsten alt om et websteds opførsel... men det er for en anden post)

Koden for denne smukke enkle:

Den sore spot her er, at hver gang nogen hits en af webstedets sider, brugerens webbrowser er at nå ud til at få elementer på listen.  Når dev er komplet og test har bevist ting at være stabil og komplet, denne indkaldelse er unødvendige mere end 99% tid siden menuen sjældent ændres.  Det har også en underlig UI indflydelse, som er almindeligt i denne fagre nye verden af hyper-ajaxy websteder – siden renders og først derefter gør menuen.  Det er nervøs og distraherende efter min mening.  Og nervøs. Så, cachelagring. 

Jeg har ændret logikken thusly:

  • Kigge efter en cookie i den browser, der indeholder menuen, som jeg sidst læste det
    • Hvis fundet, gøre det straks.  Vent ikke til siden for at afslutte læsning.  (Du skal sørge for din HTML er strategisk placeret her, men det er ikke svært at gøre).
  • Vente på siden for at afslutte lastning og gøre en asynkron hidkalde hen til ladning oppe menu artikler på en liste ved hjælp af resten eller lists.asmx eller hvad
  • Undersøg hvad jeg fik mod cookie
    • Hvis det passer, Stop
    • Ellers, ved hjælp af jQuery, dynamisk udfylde en bunke, hvis <Li>er i en <UL>
  • Brug CSS til at gøre al formatering
  • Profit!

Nogle af jer vil sige, "hey! der er ingen reel caching vej på her, da du læser menuen alligevel hver eneste gang.”  Og du har ret-jeg vil ikke give serveren nogen form for pause.  Men fordi opkaldet er asynkrone og sker efter siden 's oprindelige HTML nyttelast gengiver fuldt, det føles"" mere lydhøre over for brugeren.  Menuen gør temmelig meget som siden trækker.  Hvis menuen sker til ændringen, brugeren er udsat for en nervøs re-drage i menuen, men kun denne ene gang.

Der er nogle måder at gøre dette caching mere effektivt og hjælpe serveren på samme tid:

  • Sat i en regel at "cookie-cache" er gyldig i mindst 24 timer eller nogle andre tidsramme. Så længe der er ingen udløbet cookie, bruge den cookie menuen øjebliksbillede og aldrig ramte serveren.

Tja... det er alt, der kommer til at tænke lige nu :). 

Hvis nogen har nogen kloge tanker her ville jeg elske at kende dem.

Og endelig – denne teknik kan bruges til andre ting.  Denne klient side har en række datastyrede ting på forskellige sider, mange af dem ændre relativt sjældent (ligesom en gang om ugen eller en gang om måneden).  Hvis du er målrettet mod specifikke områder af funktionalitet, Du kan give en mere lydhør UI ved at trække indholdet fra den lokale cookie butik og rendering straks.  Det føles hurtigere til brugeren selv hvis du ikke gemmer serveren enhver cyklusser.  Du kan Gem serveren cyklusser ved at beslutte på visse betingelser og udløsere til at afkræfte denne lokale cookie cache.  Det er alle situationsbestemt og kunstnerkvarteret ting og virkelig den mest sjov :). 

</slutningen>

undefinedAbonner på min blog.

Følg mig på kvidre på http://www.twitter.com/pagalvin

Hvordan: Konfigurere enhedstest og testdækning med QUnit.js og Blanket.js For et kontor 365 SharePoint App

Intro

Jeg har været at udforske test af enheder og teste dækning for JavaScript, som jeg arbejder på en ny SharePoint app for SharePoint online i Office 365 Suite.  De åbenlyse forskning stier førte mig til Qunit.js og lige efter at, til Blanket.js.

QUnit Lad mig oprette unit-tests og gruppere dem i moduler.  Et modul er bare en simpel måde at organisere relaterede tests. (Jeg er ikke sikker på, jeg bruger det efter hensigten, men det virker for mig så langt med den lille sæt af tests jeg hidtil har defineret).

Blanket.js integrerer med Qunit og sig vil røbe sig mig de faktiske linjer af JavaScript, der var – og endnu vigtigere – var ikke faktisk udføres under kører test.  Dette er "dækning"-linjer, der udføres er omfattet af testen, mens andre ikke.

Mellem opsætning af god testcases og visning dækning, Vi kan reducere den risiko, at vores kode har skjulte defekter.  Gode tider.

Qunit

Forudsat at du har din Visual Studio projekt oprettet, starte med at hente JavaScript-pakke fra http://qunitjs.com.  Tilføje JavaScript og tilsvarende CSS til din løsning.  Mine ser sådan ud:

image

Figur 1

Som du kan se, Jeg var ved hjælp af 1.13.0 på tidspunktet skrev jeg dette blogindlæg. Glem ikke at downloade og tilføje CSS-fil.

Der ud af måde, næste skridt er at skabe en form for test sele og reference Qunit bits.  Jeg tester en masse funktioner i en scriptfil kaldet "QuizUtil.js", så jeg har oprettet en HTML-side kaldet "QuizUtil_test.html" som vist:

image Figur 2

Her er koden:

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

    <script>
        modul("getIDFromLookup");
        test("QuizUtil getIDFromLookupField", funktion () {
            varians goodValue = "1;#Paul Galvin";

            lig(getIDFromLookupField(goodValue) + 1, 2), "ID af [" + goodValue + "] + 1 bør være 2";
            lig(getIDFromLookupField(ikke defineret), ikke defineret, "Udefineret inputargumentet skal returnere udefineret resultat.");
            lig(getIDFromLookupField(""), ikke defineret, "Tom inputargumentet skal returnere en udefineret værdi.");
            lig(getIDFromLookupField("gobbledigood3-thq;dkvn ada;skfja sdjfbvubvqrubqer0873407t534piutheqw;VN"), ikke defineret,"Skal altid returnere et resultat cabriolet til et heltal");
            lig(getIDFromLookupField("2;#en anden person"), "2", "Kontrol [2;#en anden person].");
            lig(getIDFromLookupField("9834524;#lang værdi"), "9834524", "Store værdi test.");
            notEqual(getIDFromLookupField("5;#nogen", 6), 6, "Test en notEqual (5 er ikke lig med 6 for denne prøve: [5;#nogen]");

        });

        modul("htmlEscape");
        test("QuizUtil htmlEscape()", funktion () {
            lig(htmlEscape("<"), "&lt;", "Undslippe et mindre end-operatør ('<')");
            lig(htmlEscape("<div class =  "someclass">Nogle tekst</div>"), "&lt;div class =&quot;someclass&quot;&gt;Nogle tekst&lt;/div&gt;", "Mere komplekse test streng.");
        });

        modul("getDateAsCaml");
        test("QuizUtil getDateAsCaml()", funktion () {
            lig(getDateAsCaml(nye Dato("12/31/2013")), "2013-12-31T:00:00:00", "Test hårdt kodet dato: [12/31/2013]");
            lig(getDateAsCaml(nye Dato("01/05/2014")), "2014-01-05T:00:00:00", "Test hårdt kodet dato: [01/05/2014]");
            lig(getDateAsCaml(nye Dato("01/31/2014")), "2014-01-31T:00:00:00", "Test hårdt kodet dato: [01/31/2014]");
            lig(getTodayAsCaml(), getDateAsCaml(nye Dato()), "getTodayAsCaml() skal lige getDateAsCaml(ny dato())");
            lig(getDateAsCaml("vrøvl værdi"), ikke defineret, "Prøv at få datoen for en sludder værdi.");
            lig(getDateAsCaml(ikke defineret), ikke defineret, "Prøv at få datoen for den [ikke defineret] dato.");
        });

        modul("getParameterByName");
        test("QuizUtil getParameterByName (fra forespørgselsstrengen.)", funktion () {
            lig(getParameterByName(ikke defineret), ikke defineret, "Prøv at få udefineret parameter skal returnere udefineret.");
            lig(getParameterByName("findes ikke"), ikke defineret, "Prøv at få værdi for parameteren, når vi ved, at parameteren ikke findes.");

        });

        modul("Cookies");
        test("QuizUtil forskellige cookie funktioner.", funktion () {
            lig(setCookie("test", "1", -1), getCookieValue("test"), "Få en cookie, jeg indstille bør arbejde.");
            lig(setCookie("anycookie", "1", -1), True, "Indstilling af en gyldig madlavning skal returnere 'sand'.");
            lig(setCookie("crazy Cookienavn !@#$%"%\^&*(()?/><.,", "1", -1), True, "Indstilling af en dårlig Cookienavn skal returnere 'false'.");
            lig(setCookie(ikke defineret, "1", -1), ikke defineret, "Passerer udefineret som cookie-navn.");
            lig(getCookieValue("findes ikke"), "", "Cookie ikke eksisterer test.");
        });

    </script>
</hoved>
<kroppen>
    <div id= "qunit"></div>
    <div id= "qunit-armatur"></div>

</kroppen>
</HTML>

Der er flere ting der sker her:

  1. Refererer til min kode (QuizUtil.js)
  2. Refererer til Qunity.js
  3. Definere nogle moduler (getIDFromLookup, Cookies, m.fl.)
  4. Placere en <div> Hvis ID er "qunit".

Derefter, Jeg trække bare op denne side og du får noget som dette:

image

Figur 3

Hvis du ser øverst, du har et par muligheder, hvoraf to er interessante:

  • Skjul bestået test: Temmelig indlysende.  Kan hjælpe øjet bare se problemområderne og ikke en masse rod.
  • Modul: (drop): Dette vil filtrere tests ned til netop disse grupper af tests du vil.

Hvad angår prøverne sig selv – et par kommentarer:

  • Det er en selvfølge at du skal skrive din kode, så det er testbare i første omgang.  Ved hjælp af værktøjet kan hjælpe med at håndhæve denne disciplin. For eksempel, Jeg havde en funktion kaldet "getTodayAsCaml()”.  Dette er ikke meget testbare, da det tager ingen inputargumentet og teste det for ligestilling, Vi skal konstant opdatere test-kode for at afspejle den aktuelle dato.  Jeg refactored det ved at tilføje en data input-parameteren derefter passerer den aktuelle dato, når jeg ønsker dags dato i CAML format.
  • Qunit rammer dokumenterer sine egne test og det synes temmelig robust.  Det kan gøre enkle ting som test for ligestilling og har også støtte til ajax stil opkald ("rigtige" eller hånet ved hjælp af din foretrukne mocker).
  • Gå gennem processen også tvinger dig til at tænke gennem kanten tilfælde – hvad sker der med "Udefineret" eller null er gået ind i en funktion.  Det gør det døde nemt at teste disse scenarier ud.  Gode ting.

Dækning med Blanket.js

Blanket.js supplerer Qunit ved at spore de faktiske linjer kode, der udføres i løbet af kører din test.  Det integrerer ret i Qunit så selv om det er en helt separat app, det spiller pænt-det ser virkelig ud som om det er en problemfri app.

Dette er blanket.js i aktion:

image Figur 4

image

Figur 5

(Du faktisk nødt til at klikke på afkrydsningsfeltet "Aktiver dækning" øverst [Se figur 3] at muliggøre dette.)

De fremhævede linjer i figur 5 ikke er blevet henrettet af nogen af mine tests, så jeg nødt til at udtænke en test, der forårsager dem til at udføre hvis jeg ønsker fuld dækning.

Få blanket.js arbejde ved at følge disse trin:

  1. Downloade det fra http://blanketjs.org/.
  2. Føje det til dit projekt
  3. Opdatere din testside sele (QuizUtil_test.html i mit tilfælde) som følger:
    1. Reference koden
    2. Dekorere din <script> reference som denne:
    <script type= text/javascript"" src="QuizUtil.js" data-cover></script>

Blanket.js opfanger attributten "data-cover" og gør sin magi.  Det kroge til Qunit, opdaterer UI for at tilføje indstillingen "Aktiver dækning" og voila!

Resumé (TL; DR)

Brug Qunit til at skrive din prøvesager.

  • Download det
  • Føje det til dit projekt
  • Skrive en test sele side
  • Oprette din analyser
    • Refactor nogle af jeres kode hen til blive testbare
    • Være kreativ!  Tænk på crazy, umuligt scenarier og teste dem alligevel.

Brug blanket.js til at sikre dækning

  • Kontroller, at Qunit fungerer
  • Download blanket.js og føje den til dit projekt
  • Føje det til din testside sele:
    • Tilføjelse af en henvisning til blanket.js
    • Tilføje en "data-cover" attribut til din <script> Tag
  • Køre din Qunit test.

Jeg har aldrig gjorde noget af dette før og havde nogle rudimentære ting arbejder i en håndfuld timer. 

Happy test!

</slutningen>

undefinedAbonner på min blog.

Følg mig på kvidre på http://www.twitter.com/pagalvin