31 August 2006

TDD web controls dev life cycle

An entire upside down experience to help coder focus on deliver 'good enough' requirements

In summary this dev pattern is

1. decide deliverables - the rendered html as browser user sees. (step 1, 2)
2. write ruby script to test the html (in fact it is reversed test the test script) (step 3)
3. act as web designer to use the web control to build a test page (of course, the web control is not existed yet) (step 4)
4. Implment web control (step 5)
5. test the web control (step 6-7)
6. change loop

A well designed website should be broken into three domains:
1) Reusable Web controls which render plain html
2) bare bone website composed of web controls. Pages are served as control containers and responsible for managing navigation, user experience. It should contains no inline css but class/id/name that allows UI designer change the 'skin' without touching the website, not even a downtime.
3) CSS

Here is the Test Driven Development Cycle
1. Agree with UI designer on html template. the static html prototype is the 'contract'.

2. break down the template into componentised controls that will be generated with user/custom controls. Save the html corresponding to the control to a new html e.g. ~\gallery\searchbox.html
<html>
<body>
<!--search box-->
<div id="PARENTDIV" name="PARENTDIV" class="TestGlobalStyleClass">
<label id="PARENTDIV.MessageLabel" name="PARENTDIV.MessageLabel">TestMessageLabelText</label><br>
<input type="text" id="PARENTDIV.SearchTerm" name="PARENTDIV.SearchTerm" class="SearchTermClass" size="100" maxlength="100">
<a href="http://test.webcontrols.com" id="PARENTDIV.AdvLink" name="PARENTDIV.AdvLink"
title="Test ToolTip" class="AdvLinkClass">Test Link Text</a>
<!--search box close--></div>
</body>
</html>

3. Write an Ruby script 'SearchBoxTestFixture.rb' to test this template. This is to validate our test case is sounds.

require 'watir'
require 'test/unit'
require 'test/unit/assertions'
include Watir

class SearchBoxTestFixture <>
def setup
localHostXSP = "http://127.0.0.1"
localHostIIS = "http://localhost/Talis.Web.Cenote.WebControls.Test"
@staticTemplate = 'http://localhost/Talis.Web.Cenote.WebControls.Test/static/searchbox.html'
remoteHost = "http://talis.com";
@testSite = localHostIIS
@ie = IE.new
end

def teardown
@ie.close
end

def test_allHtmlElementsExist
# page = "SearchBoxTest.aspx"
# @ie.goto(@testSite+'/'+page)

# test static html template
@ie.goto(@staticTemplate)

assert_equal(1, @ie.divs.length, 'Expecting only one div')
assert(@ie.div(:id, /PARENTDIV/).exists?, "Expecting div id 'PARENTDIV'")
assert(@ie.div(:name, /PARENTDIV/).exists?, "Expecting div name 'PARENTDIV'")

#assert message label
assert_equal(1, @ie.labels.length, 'Expecting only one lable box')
assert(@ie.label(:id, /PARENTDIV.MessageLabel/).exists?, "Expecting label id 'PARENTDIV.MessageLabel'")
assert(@ie.label(:name, /PARENTDIV.MessageLabel/).exists?, "Expecting label name 'PARENTDIV.MessageLabel'" )
assert('TestMessageLabelText', @ie.label(:id, "PARENTDIV.MessageLabel").innerText)

#assert input box
assert_equal(1, @ie.text_fields.length, 'Expecting only one input box')
assert(@ie.div(:id, /PARENTDIV/).text_field(:id, 'PARENTDIV.SearchTerm').enabled?, "Expecting textbox id 'PARENTDIV.SearchTerm'" )
assert(@ie.text_field(:name, 'PARENTDIV.SearchTerm').exists?, "Expecting textbox name'PARENTDIV.SearchTerm' textbox element")
assert_equal(100, @ie.text_field(:id, 'PARENTDIV.SearchTerm').size(), "Expecting textbox size 100")
assert_equal(100, @ie.text_field(:id, 'PARENTDIV.SearchTerm').maxLength(), "Expecting textbox maxLength 100")

#assert link
assert_equal(1, @ie.links.length, 'Expecting only one link')
assert(@ie.link(:id, 'PARENTDIV.AdvLink').exists?, "Expecting link id 'PARENTDIV.AdvLink'" )
assert(@ie.link(:name, /PARENTDIV.AdvLink/).exists?, "Expecting link name 'PARENTDIV.AdvLink'" )
assert_equal('http://test.webcontrols.com/', @ie.link(:name, /PARENTDIV.AdvLink/).href, "Expecting link url 'http://test.webcontrols.com'" )
assert(@ie.link(:title, 'Test ToolTip').exists?, "Expecting link title (tool tip) 'Test ToolTip'" )
assert_equal('Test Link Text', @ie.link(:id, 'PARENTDIV.AdvLink').innerText, "Expecting link text 'Test Link Text'" )

end
end

4. write SearchBoxTest.aspx that intend to render as to searchbox.html. Ours contains this:
<My:SearchBox
ID="PARENTDIV"
Class="TestGlobalStyleClass"
MessageLabel_Text="TestMessageLabelText"
MessageLabel_Class="TestMessageLabelClass"
SearchTerm_Class="SearchTermClass"
SearchTerm_Size="100"
AdvLink_Class="AdvLinkClass"
AdvLink_Tooltip="Test ToolTip"
AdvLink_InnerText="Test Link Text"
Runat="server"
/>

5. Implement the custom web control: SearchBox.cs

6. If haven't done so, write nUnit Testfixture to hook* ruby script into your test dll. So all tests will feedback as red/green light

7. Modify SearchBoxTestFixture.rb to target SearchBoxTest.rb.

Contract changed, start from 1. again

Notes:
1. You need to install Ruby WATIR
2. Travis Illig has this fatastic RubyTestExecutor integrates ruby and watir test scripts into nUnit test framework. Check his paper Integrated ASP.NET Web Application Testing with NUnit, Ruby, and Watir on code project.

No comments: