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.

30 August 2006

Mono is not an adaptive asp.net web rendering engine

Scott Mitchell writes in ASP.NET.4GuysFromRolla.com: A Look at ASP.NET's Adaptive Rendering: IIS ASP.NET renders web controls by first detect the user-agnet type, for this reason, ASP.NET Web controls are called adaptive. By default it renders HTML 3.2-compliant markup using tagwriter=System.Web.UI.Html32TextWriter. For
HTML 4.0-compliant agents (Mozilla/4.0 and above, e.g. MSIE6) it uses
tagwriter=System.Web.UI.HtmlTextWriter. IIS does so by having a regex check for browser User-Agent. However, the default implementation doesn't address FireFox 0.8 and above, which is Mozilla/5.0 and HTML 4.0-compliant.
Here we will check the adaptive rendering issue around Mono1.x ASPNET instead of IIS.

Default behaviour
1. Test rendering by Mono XSP web server.
On MSIE 6.0: tagwriter=System.Web.UI.HtmlTextWriter
evidence: System.Web.UI.WebControls.WebControl is rendered as <div>
On FireFox 1.5: tagwriter=System.Web.UI.HtmlTextWriter
evidence: System.Web.UI.WebControls.WebControl is rendered as <table >
Mono1.1 doesn't address adaptive Rendering - there is no <browserCaps> in its machine.config

2. Test rendering by IIS6
On MSIE 6.0: tagwriter=System.Web.UI.HtmlTextWriter
evidence: System.Web.UI.WebControls.WebControl is rendered as <div>
On FireFox 1.5: tagwriter=System.Web.UI.Html32TextWriter
evidence: System.Web.UI.WebControls.WebControl is rendered as <table ><tr><td>

It seemed that Mono team get around the problem luckily because it was born at a time HTML 4.0-compliant browsers prevails. (At least it was what they thought?). But maybe we can still add <browserCaps> for backward adaptive? At least it looks straightforward enough to add Add <browserCaps> to web.config to enable mono becomes aptive.
Not quite though.
Checking native .net1.x machine.config, in <configuration><configSections> there is:
<section name="browserCaps" type="System.Web.Configuration.HttpCapabilitiesSectionHandler, System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
HttpCapabilitiesSectionHandler handles <browserCaps>. However, mono1.x build System.Web does NOT contain type HttpCapabilitiesSectionHandler implementation.

Conclusion: Mono1.X is not an adaptive ASPNET rendering engine.

Ideally we could add this to Web.config (affect single web app) or machine.config (affect all web apps on the machine)

<configuration>
...
<configSections>
...
<sectionGroup name="system.web">
<section name="browserCaps" type="System.Web.Configuration.HttpCapabilitiesSectionHandler , System.Web, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
...
</sectionGroup>
</configSections>
<System.Web>
...
<browserCaps>
<!-- if (working on web.config) { copy all <browserCaps> section from machine.config} -->
<!-- copy Rob Eberhardt's <browserCaps> (http://slingfive.com/pages/code/browserCaps/browserCaps_spaces.txt ) -->
</browserCaps>
...
</System.Web>

Technorati Tags: MONO ASPNET

27 August 2006

Intellisense Nant Build Script on VSNET

From Serge van den Oever
1. Edit and save following build script to NAntGenerateSchema.build. This
script is used to generate the nant.xsd for the Intellisense trick
.
<?xml version="1.0" encoding="utf-8" ?> <project
name="GenerateNAntSchemaForVS.NET" default="genschema"> <property
name="myVsNetRoot" value="C:\Program Files\Microsoft Visual Studio .NET 2003"
/> <property name="nantSchema"
value="${myVsnetRoot}\Common7\Packages\schemas\xml\NAnt.xsd"/> <target
name="genschema"> <nantschema output="${nantSchema}" target-ns=" http://nant.sf.net/schemas/nant.xsd"/>
</target> </project>

1) update property myVsNetRoot to your VSNET install path.

2) save this script as NAntGenerateSchema.build

3) run "nant NAntGenerateSchema.build ' . This will download the latest nant schema from Source Forge to VSNET schema dir. Note: this script should be run when refreshing nant releases.

2. Suppose you are working on HelloWorld.sln with VSNET, create a HelloWorld.build, which is your nant build script. Open HelloWorld.build in the IDE ((RC) by open with - HTML/XML Editor (set to default unless you need encoding).

3. Open the properties window for HelloWorld.build, Select " http://nant.sf.net/schemas/nant.xsd" as TargetSchema.

Job done.

Technorati Tags: NANT VSNET

26 August 2006

nAnt build script to test mono web app with Ruby and Watir

Download the build script template: myWebsite.build

1. Prove nant task <csc>, <nunit2> works/doesn't work with mono
build

  1. Prove test assembly is fine use vsnet (.net 1.1) build, reference *.net1.1*
    nunit.framework.dll , not the mono build.

    • build asm
    • tested with nUnit2.2.0 GUI exe
    • tested with nUnit-2.2.0-mono console exe.

    Both work as expected

  2. nAnt build. nant target mono-1.0; reference lib (nunit.framework.dll) set to NUnit-2.2.0-mono

    • test assembly with nUnit2.2.0 GUI exe. On loading asm it throws exception
      'This assembly was not built with the NUNIT framework and contains no tests'
      cases' on the popout message window
    • test assembly with nUnit-2.2.0-mono
      console exe. This returns:
      OS Version: Microsoft Windows NT 5.1.2600.0 .NET Version: 1.1.4322.2032 Tests run:
      0, Failures: 0, Not run: 0, Time: 0 seconds Open the assembly by .net
      reflector, surprise, surpise there is nothing in the dll
2. Use mono mcs compiler
  1. compiles the source
    mcs -r:system.dll -r:\sslocal\Cenote.root\dependencies\NUnit-
    2.2.0-mono\bin\nunit.framework.dll -t:library SampleRubyTestFixture.cs

  2. test - test assembly with nUnit2.2.0 GUI exe. - test assembly with
    nUnit-2.2.0-mono console exe. - test assembly with nUnit2.2.8 GUI exe. All work
    as expected

  3. Open the assembly by .net reflector, it confirms that our test case is in there This confirms that nUnit2.2.* GUI exe doesn't discriminate mono build, as long as nunit.framework.dll is the .net1.1 compliance version This also confirms that the problem is not with nant task <nUnit2>, regardless which nunit build version it points to.
3. Questioning nant task <csc>
Before setting off to use task <exec> and mcs for the build, I have a few more go on task <csc>. Eventually it confirms that it was a bug in the <csc> task causes the problem.
nAnt doesn't support multiple filesets or its derived types like assemblyfileset etc, which means you cann't resuse reference assemblyfileset. For building multiple projects in a build that each project has an inter-set to others, each <csc> needs to have a full list of <reference><include> dlls. A bit of pain. Using <module> is a hack. Though it solves the resuse problem on the surface, if you poke the assembly with Reflector, you will find there are modules should really be references. this is NOT the right way to reference <assemblyfileset>.
<csc> ... <references refid="sys.assemblies " /> <modules>
<include name="${webcontrols.output.Mono}\${webcontrolsNamespace}.dll" />
<include name="${cenote.output.Mono}\${cenoteNamespace}.dll" />
</modules> </csc>

By the way, if declare <fileset>and its derived types like
<assemblyfileset> at project level(doc root) it can take an id attribue
and can be referenced from everywhere.
4. <nunit2> and nUnit2.2.x GUI
Leaving the bug in <csc> behind, I move into nant <target> unit
test with <nunit2>. nunit2 used nunit-version=" 2.2.8.0"
clr-version="2.0.50727.42" for the test. Passed. However, now the mono-target
assemble is not loaded with nUnit2.2.X GUI, not even nUnit-2.2.0-mono console
exe. An System.IO.FileNotFoundException is thrown, complaining test dll or its
dependencies dlls are not found. [to-do] I will look into it later. This is not
an issue for next step. (We only use nUnit gui for vsnet build)
5. Serve web test using ruby+watir
prerequisite: install Ruby
engine and Watir library
Watir offers a clean, well define, automated and scripting (in opposite to
recording) base Web app test.
Travis
made a nice hook to integrate ruby script into nUnit test
framework. It is not absolutely necessary to integrate ruby script into nUnit.
*.rb script can run from cmd line. However, the integration leverage the
reporting function comes with nUnit.
  1. compile RubyTestExecutor-1.1.3.0 to target nunit_2.2.0_mono
  2. Write a 'Hello World' test case as well as ruby script. In Nant task
    <csc>include them as embeded resource. <resources
    dynamicprefix="true"> <include name="...\*.rb" /> </resources>
  3. VSNET .net1.1 build and nUnit GUI test

    • in vsnet reference RubyTestExecutor-1.1.3.0_target_nunit_2.2.0
    • make sure the Web_Control_test_project is set to default web sharing. It
      contains test pages as well as test cases.
    • Postbuild action to copy test.dll and referenced dlls (except
      nUnit.framework.dll) from ~\bin\Debug to ~\bin
    • run tests

  4. VSNET mono1.0 build and nAnt build (test with <nunit2>, on my pc it picks
    the lastest nunit2.2.8 engine)

    • in nAnt build reference RubyTestExecutor-1.1.3.0_target_nunit_2.2.0_mono
    • compile, delpoy as usual
    • start XSP at Web_Control_test_project where pages live.
    • Both use port 80 for http request. This means we don't need to change ruby
      scripts for native 1.1 (which runs on IIS) or mono runtime (which runs XSP).
      Just need to stop/start IIS before XSP kicks start. (I argue it is more
      pragmatic then dynamic decide which port to use depends on runtime env)
5 Conclusion
With this build script, we archieve:
  • Implement, build and test ASP.Net Web Controls and Website in VSNET2003 IDE to
    target .Net 1.1 framework.
  • Use nAnt build script to link, compile, deploy the same code base to target
    Mono1.0. This build process needs to know nothing about the IDE build.
  • Use Ruby+Watir scripts to test web app in 'nearest' real way.The script test
    suite is seamlessly integrate into nUnit framework.
6 What's more can be done?
Automated XSP web server start/stop. The build process requires manually start
mono XSP web server before <nunit2> tests. XSP must run in an isolated
AppDomain/process from where nAnt is running from, so use <exec> task is
not prossible. It is also not possible to use
Application Domain
ExecuteAssembly
to create and load XSP into an
isolated AppDomain because on Windows OS XSP requires mono runtime environment,
which is unmanaged code. (Though XSP.exe is managed code.)

Download the build script template: myWebsite.build

Technorati Tags: nAnt

20 August 2006

TDD Mono asp.net web application on

Note: (This is a dump as I play with the new tools on doing familiar TDD aka. Test Driven Development), work in progress.

The application:
An asp.net web application run on Linux Mono

Requirement: Unit test web application UI.

This is hard. NUNIT is designed for API (exe, lib) test

Unit test server controls

Support Continuous integration


Tools for the trade:

- VS.net IDE

- NUnit (For this project we targeting asp.net 1.1, so pick NUnit 2.2 from the list)

- Nant

- Ruby

- WATIR (I use watir-1.4.1.exe as at the time of writing)

- Mono gtk, XSP web server


Internal Resource:

- Ruby test code template for vs.net

- Water code template for vs.net (because I am lazy)


Reference:

Babysteps in WATIR A Jumpstart to Ruby/Watir

Integrated ASP.NET Web Application Testing with NUnit, Ruby, and Watir Travis Illig creates a RubyTestExecutor to integrate Ruby/Watir Testscripts into nUnit framework. So test cases can be run from GUI like other nUnit test cases. However this is more of convenience that necessary. You still write Ruby/Watir scripts. You can still invoke them from command line.

Introduction to Mono - Your first Mono app Happened to find this Primer


Development pattern:


Assumption: Coding in vs.net 2003, OS: winXP

(One off task) Create web project

  1. create project directory: {root}\My.Hello.World
  2. Web share this folder
  3. create a new web project in vs.net, map it to My.Hello.World
  4. Execute Mono run time test. Bring up command line console and do nAnt build.
    a. Start Mono XSP web server
    b. nAnt target doesn't natively require nUnit, although it could leverage point 1-3.
    c. However, this is redundant. We can just load up Ruby and Watir script engines and call test scripts
Technorati Tags: TDD RUBY WATIR NUNIT ASPNET

15 August 2006

svn external steps

This quick note gives a step by stop guide to make svn external reference.
svn external means that local copy is a references to repository rather than a working copy. So if later checking in would not check-in the local copy, but mark the reference only.
1. {local path}mkdir dependencies. dependencies contains a collections of external denpendencise (references) we will import next.
2. svn add dependencies {repository url}, and
3. svn ci -m"..." dependencies. 2. 3. source controls 'dependencies'
4. (use windows file browser), go to 'dependencies' |(right click)|Properties|Subverion tab| (requires Tortoise, the svn windows client)
5. from the Subverion tab, select 'svn externals' from middle dropdown box, then in the text area below, type '3rdAPILib url_to_3rdAPILib' (no*'*, 3rdAPILib is the dir name you give to the external reference, url_to_3rdAPILib is its location in the repository)
6. click 'set' button, then 'ok' it.
7. right click 'dependencies'|'SVN Update'. This should get a copy of '3rdAPILib' to your {local path}\dependencies .
Technorati Tags: <a href="http://technorati.com/tag/Subversion" rel="tag">Subversion</a> <a href=" http://technorati.com/tag/SVN" rel="tag">SVN</a>

06 August 2006

Google 1.0 vs Yahoo 2.0

In the book The Search, John Battelle compares Yahoo and Google:


"Yahoo makes no pretence of objectivity-it is clearly steering searchers toward its own editorial services, which it believes can satisfy the intent of the search. In effect, Yahoo is saying 'You're looking for stuff on Usher? We got stuff on Usher, and it's good stuff. Try what we suggest; we think it'll be worth your time."


"Apparent in that sentiment lies a key distinction between Google and Yahoo. Yahoo is far more willing to have overt editorial and commercial agendas, and to let humans intervene in search results so as to create media that supports those agendas. Google, on the other hand, is repelled by the idea of becoming a content- or editorially driven company…they approach the task with vastly different stances. Google sees the problem as one that can be solved mainly through technology-clever algorithms and sheer computational horsepower will prevail. Humans enter the search picture only when algorithms fail and then only grudgingly."


What Battelle makes is that Google's approach – using technology- 'the machine' and algorithm to solve indexing world's information. I tend to think of it as 'content based' search: index based on content text rather than the semantic meaning of it and search based on keyword appearances and PageRank to decide its weight.


On the other hand Yahoo is taking an editorial approach on searching. It integrates human to drive search, helping searchers force on search intention. 'What you are really looking for?' By typing search keyword 'Usher' do you mean music artist Usher's lyrics or buying an Usher' music CD?


In my opinion this intention based search is one step higher than content based search, though it intrinsically comes with scalability issue. How many people Yahoo needs to satisfy the world?


Here is where Web 2.0 cutting in. The essence of Web 2.0 is collaborate and share information. Build social network by interaction of surfers. Web users self-govern, actively participate in virtual communities, engaging with each other. Web users can also help each other on driving intent based search using new emerging technology like tagging: Reading something interesting/disgusting? Right click the mouse on the page and throw in a keyword, which is then stored in an indexing machine (may well be from Yahoo or Google) together with the URL. On the back of this, the index machine scan and sort the tag with other tags that already add to this article (URL) and apply some smart algorithm…


Technorati Tags:

Content or intention?

How does the news industry "cross the chasm" and survive in a search-driven world? I don't have a silver bullet, unfortunately, but it starts by opening up its sites and realizing that in a post-Web world, the model for news is no longer site driven. Sites that wall themselves off are becoming irrelevant, not because the writing or analysis is necessarily flawed, but rather because their business model is. In today's ecosystem of news, the greatest sin is to cut one-self off from the conversation. Both The Economist and the Wall Street Journal have done just that
...
Remarking how traditional subscription based online media would be benefit from open up deep linking - allowing search and linking to their walled assert - subscription protected content:

The goal is to make content that is worth pointing to. If you're feeding the conversation, the rest will then follow, including advertisers who want to be in the conversation that news stories are fostering.

from The Search, John Battelle