33
TEST DRIVEN DEVELOPMENT TIMOTHY BOLTON

Test driven development (java script & mivascript)

  • Upload
    miva

  • View
    340

  • Download
    1

Embed Size (px)

Citation preview

TEST DRIVEN DEVELOPMENT

TIMOTHY BOLTON

Test Driven Development

•What is TDD?•Why use TDD?•How to use TDD?•When to stray from TDD?•Super Simple Sample of TDD•Recap

What is TDD?

•Development “driven” by testing.–Tests are written before code is.–Tests should initially fail–Production code should be enough to pass

•A simple philosophy•A great way to make you feel superior

Why use TDD?

•It makes refactoring very simple.•It gives you confidence•It helps create a scope of work•It creates a baseline•It helps you document your project•It helps bring new developers up to speed

How to use TDD?

•There are 3 Rules1.No production code unless it makes a failing test pass.2.No more of a unit test written than is sufficient to make it fail.3.No more production code is written than is sufficient to pass the one failing unit test.

How to use TDD cont’d

•Build your own tools–My Environment consists of:

•Vim•Tmux•watchTests.sh Script•Inotifywait

When to stray from TDD?

•Ideally, “never”•Realistically?

–Working on a pre-existing project–Working in a language where TDD frameworks are not established–When tests take too long. It’s the truth. :’(

Super Simple Sample of TDD

•Email address availability with pure MivaScript and JavaScript•Remember

–We don’t have a lot of time–We won’t write a lot of tests for MivaScript–We don’t have tools built for Mocking

•https://bitbucket.org/snippets/timothybolton/BdL8

Super Simple Sample Cont’d

•Where to start?–MivaScript

•Create a simple “AssertTrue” function•Create a simple queryEmailTests.mv file

–Put some lipstick on that pig with formatting–Write the first test

•Create production code

Super Simple Sample Cont’d

•What are we trying to accomplish?See if an email address is used already

•Moving parts and external dependencies–Database Connection–Data in Database–Network Connection

Super Simple Sample Cont’d

MivaScript “AssertEqual” functionWhile we certainly don't need a function to do this, as

we can make simple assertions with “MvIF” statements, it makes the code easier to read and communicates the intention.

Other AssertionsAssertTrue, AssertFalse, AssertNull, AssertNotNullAssertNotEqual, AssertIsDigit, AssertIsAlpha, etc.Be creative. Work together. Write better scripts.

Super Simple Sample Cont’d

FILE NAME: mvassert.mv1 <MIVA STANDARDOUTPUTLEVEL = "text, compresswhitespace">2 <MvFUNCTION NAME = "AssertEqual" PARAMETERS = "expected, actual" STANDARDOUTPUTLEVEL="" ERROROUTPUTLEVEL="">3 <MvFUNCTIONRETURN VALUE = "{ l.expected EQ l.actual }">4 </MvFUNCTION>

Super Simple Sample Cont’d

FILE NAME: mvTestLibrary.mv1 <MIVA STANDARDOUTPUTLEVEL = "html, compresswhitespace">23 <MvFUNCTION NAME = "generateTestResults" STANDARDOUTPUTLEVEL = "html, compresswhitespace">4 <MvEVAL EXPR = "{ buildViewStart() }">56 <MvFOR INDEX = "l.pos" FIRST = "{ 1 }" NEXT = "{ ++l.pos }" LAST = "{ g.currentTestNumber }">7 <MvASSIGN NAME = "l.currentTestStatusStyle" VALUE = "{

generateTestStatusStyle(g.testResults[l.pos]:status) }">8 <MvEVAL EXPR = "{ generateTestRowData(g.testResults[l.pos]:name, l.currentTestStatusStyle) }">9 </MvFOR>10 <MvEVAL EXPR = "{ buildViewEnd() }">11 </MvFUNCTION>1213 <MvFUNCTION NAME = "generateTestRowData" PARAMETERS = "name, style" STANDARDOUTPUTLEVEL ="compresswhitespace">14 <MvIF EXPR = "{ g.source EQ 'curlVim' }">15 <MvASSIGN NAME = "l.view" VALUE = "{ generateTestRowDataCli(l.name, l.style) }">16 <MvELSE>17 <MvASSIGN NAME = "l.view" VALUE = "{ generateTestRowDataHtml(l.name, l.style) }">18 </MvIF>19 <MvFUNCTIONRETURN VALUE = "{ l.view }">20 </MvFUNCTION>2122 <MvFUNCTION NAME = "generateTestRowDataHtml" PARAMETERS = "name, style" STANDARDOUTPUTLEVEL ="compresswhitespace">23 <MvFUNCTIONRETURN VALUE = "{ '<tr><td>' $ l.style $ '</td><td>' $ l.name $ '()</td></tr>' }">24 </MvFUNCTION>2526 <MvFUNCTION NAME = "generateTestRowDataCli" PARAMETERS = "name, style" STANDARDOUTPUTLEVEL ="compresswhitespace">27 <MvFUNCTIONRETURN VALUE = "{ '28 ' $ l.style $ ' ' $ l.name $ '()' }">29 </MvFUNCTION>3031 <MvFUNCTION NAME = "buildViewEnd">32 <MvIF EXPR = "{ g.source EQ 'curlVim' }">33 <MvASSIGN NAME = "l.view" VALUE = "{ buildViewEndCli() }">

Super Simple Sample Cont’d

FILENAME:queryEmailTests.mv 1 <MIVA STANDARDOUTPUTLEVEL = "text">2 <MvINCLUDE FILE = "mvassert.mv">3 <MvINCLUDE FILE = "mvTestLibrary.mv">4 <MvINCLUDE FILE = "queryEmailFunctions.mv">5 <MvASSIGN NAME = "g.currentTestNumber" VALUE = 0>7 <MvCOMMENT>Tests are run here</MvCOMMENT>8 <MvASSIGN NAME = "l.result" VALUE = "{ testEmailAvailableOnAddress() }">10 <MvEVAL EXPR = "{ generateTestResults() }">11 <MvEXIT>13 <MvCOMMENT>Tests are written here</MvCOMMENT>14 <MvFUNCTION NAME = "testEmailAvailableOnAddress" STANDARDOUTPUTLEVEL = "compresswhitespace">15 <MvASSIGN NAME = "g.currentTestNumber" VALUE = "{ ++g.currentTestNumber }">16 <MvASSIGN NAME = "g.testResults" MEMBER = "status" INDEX = "{ g.currentTestNumber }" VALUE = "{ AssertEqual(1, CheckEmailAvailable('[email protected]')) }">17 <MvASSIGN NAME = "g.testResults" MEMBER = "name" INDEX = "{ g.currentTestNumber }" VALUE = "{ 'testEmailAvailableOnAddress' }">18 </MvFUNCTION>

Super Simple Sample Cont’d

Vim integration (put in your .vimrc, and validate for your sites)nnoremap <Leader>ca :! mvc % && curl -T %c ftp://ftp.mivamerchantdev.com/mm5/5.00/modules/util/ -u [email protected]@dts2639.mivamerchantdev.com:$MIVAPASSWORD && clear && curl http://dts2639.mivamerchantdev.com/mm5/5.00/modules/util/%c?Store_Code=MIVA_DEV\&source=curlVim<CR>

Super Simple Sample Cont’d

We have our first test, let's try to run it.What do you think will happen?That's right… it won't work.We need to write our production function.

Super Simple Sample Cont’d

Boiler Plate CodeFILENAME: queryEmailFunctions.mv1 <MIVA STANDARDOUTPUTLEVEL = "text, compresswhitespace">2 <MvCOMMENT> 99% of this code is from the LSK or the fora.</MvCOMMENT>3 <MvDO FILE = "../../lib/config.mvc">45 <MvIF EXPR = "{ NOT [ '../../lib/db.mvc' ].OpenDataFiles( g.Merchant_Version, g.Domain ) }">6 <MvEXIT>7 </MvIF>89 <MvEVAL EXPR = "{ [ '../../lib/util.mvc' ].SetRuntimePaths() }">1011 <MvIF EXPR = "{ NOT [ g.Module_Library_DB ].Store_Open( g.Store_Code, g.Store ) }">12 <MvIF EXPR = "{ NOT g.Store_Code }"> <MvEVAL EXPR = "{ 'YOU NEED TO SUPPLY g.Store_Code' }"> </MvIF>13 <MvEXIT>14 </MvIF>

Super Simple Sample Cont’d

16 <MvFUNCTION NAME = "CheckEmailAvailable" PARAMETERS = "email" STANDARDOUTPUTLEVEL = "" ERROROUTPUTLEVEL ="">17 <MvOPENVIEW NAME = "Merchant"18 VIEW = "Customers"19 QUERY = "{ 'SELECT COUNT(*) AS emailUsed '20 $ 'FROM ' $ g.Store_Table_Prefix $ 'Customers '21 $ 'WHERE ' $ [ g.Module_Library_Native_DBAPI ].DB_Compare_UPPER( 'pw_email' )22 $ ' = '23 $ [ g.Module_Library_Native_DBAPI ].DB_Compare_UPPER( '?' ) }"24 FIELDS = "l.email">25 <MvIF EXPR = "{ Customers.d.emailUsed GT 0 }">26 <MvASSIGN NAME = "l.returnValue" VALUE = "0">27 <MvELSE>28 <MvASSIGN NAME = "l.returnValue" VALUE = "1">29 </MvIF>30 <MvCLOSEVIEW NAME = "Merchant" VIEW = "Customers" >31 <MvFUNCTIONRETURN VALUE = "{l.returnValue }">32 </MvFUNCTION>

Super Simple Sample Cont’d

What happens when we run now?Using our Vim plugin, we can run from within

our editor, from the CLI, or via the web. The mvTestFramework has detection from passed in parameters, to see the output we specify.

Super Simple Sample Cont’d

SeparationSeparation function declarations from side-effectsThis make testing easier

Our compiled script1 <MIVA STANDARDOUTPUTLEVEL = "text, compresswhitespace">2 <MvINCLUDE FILE="queryEmailFunctions.mv">4 <MvEVAL EXPR = "{ CheckEmailAvailable(g.username) }">5 <MvEXIT>

Super Simple Sample Cont’d

Test again.Now we can rinse and repeat with another

test.

Super Simple Sample Cont’d

JavaScript TestingAjax can be difficult without mocking, or

using synchronous calls. Which would then be “Jax”… because, there is no 'A'.This is depreciated because it degrades the

user experience, but it can be useful for testing.

Super Simple Sample Cont’d

Our simple assertion library will leverage some of the console behavior that comes with Chrome and other browsers.We will just create a simple tdd page that we

can protect.

Super Simple Sample Cont’d

1 var testAjax = new XMLHttpRequest();23 testAjax.onreadystatechange = function() {4 if(testAjax.readyState == 4 && testAjax.status == 200) {5 return parseInt(testAjax.responseText);6 }7 }89 function testAvailableEmail(email) {10 testAjax.open("GET", "5.00/modules/util/queryEmail.mvc?store_code=MIVA_DEV&username=" + email, false);11 testAjax.send();12 return { "test": arguments.callee.name,13 "actual": testAjax.responseText,14 "expected": 115 };16 }1718 function testUnavailableEmail(email) {19 testAjax.open("GET", "5.00/modules/util/queryEmail.mvc?store_code=MIVA_DEV&username=" + email, false);20 testAjax.send();21 return { "test": arguments.callee.name,22 "actual": testAjax.responseText,23 "expected": 024 };25 }2627 var testInformation = new Array();2829 testInformation.push(testAvailableEmail('[email protected]'));30 testInformation.push(testUnavailableEmail('[email protected]'));3132 for(var i = 0; i < testInformation.length; i++) {33 console.assert(testInformation[i].actual == testInformation[i].expected, testInformation[i].test + ' Failed. '34 + 'Actual: ' + testInformation[i].actual + '. Expected: ' + testInformation[i].expected );35 }

Super Simple Sample Cont’d

17 function testUnavailableEmail(email) {18 testAjax.open("GET", "5.00/modules/util/queryEmail.mvc?store_code=MIVA_DEV&username=" + email, false);19 testAjax.send();20 return { "test": arguments.callee.name,21 "actual": testAjax.responseText,22 "expected": 0 };23 }

Super Simple Sample Cont’d

27 testInformation.push(testAvailableEmail('[email protected]'));28 testInformation.push(testUnavailableEmail('[email protected]'));2930 for(var i = 0; i < testInformation.length; i++) {31 console.assert(testInformation[i].actual == testInformation[i].expected,32 testInformation[i].test + ' Failed. '33 + 'Actual: ' + testInformation[i].actual + '. Expected: ' + testInformation[i].expected );34 }

Super Simple Sample Cont’d

We are ready to put up some production code now.

Super Simple Sample Cont’d

1 var newAjax = new XMLHttpRequest();23 newAjax.onreadystatechange = function() {4 if(newAjax.readyState == 4 && newAjax.status == 200) {5 responseAction(newAjax.responseText);6 }7 }89 var email = document.getElementById('Customer_LoginEmail');1011 email.onchange = function() {12 newAjax.open("GET", "5.00/modules/util/queryEmail.mvc?store_code=MIVA_DEV&username=" + this.value);13 newAjax.send();14 }1516 function responseAction(response) {17 var alertText = "";18 if(parseInt(response) == 0) {19 alertText = "That username is unavailable";20 } else {21 alertText = "that username is available";22 }23 alert(alertText);24 }

Super Simple Sample Cont’d

We are ready to put up some production code now.We have successfully test driven developed.

Exercises

Refactor this to use JSONAdd more testsAdd more functionality

Parting Thoughts

See the value in your tests now that they are reusable.It is annoying at first, but it pays off. Tests like

this have small upkeep, and give you a lot of freedom to experiment.I hated TDD at first. Now, I think it's “okay”

Thank you!

TIMOTHY BOLTON