Thursday, April 19, 2007

Test your web application with Selenium

Thanks to Skills Matter I attended a Selenium session tonight. Selenium is a tool for automated web testing. The session was led by Erik Doernenburg. He's a ThoughtWorker, so I knew the presentation was going to be good quality. Not only did he present what Selenium is, but he also presented many good techniques for using it with your project.
Some of my notes below:
  • All important browsers are supported
  • All important languages are supported
    • He used Java at the talk
  • SeleniumIDE is the tool for recording tests
    • It's not necessary to use it, you can write tests in your favourite language
    • SeleniumIDE is only for Firefox
  • You need to start the Selenium server in order to run tests
  • Many methods available:
    • createCookie
    • dragdrop
    • fireEvent
    • selenium.click("btnG")
    • assertTitle
  • There is a concept of locators
    • It means the way to locate your DOM elements
    • name, id, identifier
    • document.forms['myForm'].myDropDown
    • XPath
    • link text
    • css selectors, css = a[href="#id3"]
  • Selenium server sets the proxy in your browser, so your browser asks the server for everything
  • It's bad for performance testing
  • Good for testing multiple browsers
  • There was some great advice, which I think applies for all kinds of functional tests
    • "Test what you want to test"
    • Which means, if you have already tested creating users in a functional way, you don't need to test it functionally again
    • So, you can actually use your production code to change the state and then functionally test what you're actually testing
    • Saves time
    • Makes your tests less fragile
    • Cool!
  • It's a good pattern to have classes for all of your pages
    • WelcomePage
    • DailyViewPage
  • And one class for your test
    • AddItemToBasket
    • CreateUser
  • Your test classes will then use your page classes
    • welcomePage = WelcomePage()
    • welcomePage.setDailyView()
  • There are other ways of functionally testing your web application
    • HttpUnit which asks your web server, not the browser
    • Use a Presentation pattern
      • have only thin presentation layer
      • test only your "event handlers" methods
  • It's common practice for QA people to create Selenium scenarios
  • You can start your TDD session with a failing Selenium test

Monday, April 16, 2007

RuPy 2007

  • A well-organized conference
  • It was interesting to meet so many "dynamic" people
  • Many Ruby people attended Python talks and vice-versa
  • Maybe instead of having two separate sessions it would be better to have one with shorter talks?
  • Poznan is a very nice city, almost as nice as Wroclaw :)
  • The quote of the conference:
    • "Look, all of the Ruby people have Macs!"
    • "Yeah, they are better paid..."


Christopher Arndt talk on TurboGears
  • TG is very similar to Rails
  • TG has support for testing, unfortunately it wasn't shown during the talk
  • as with all (?) Python web frameworks, you can't use Python for implementing views
  • it's better with Rails, that you don't have to change the language for your views
  • It uses SqlObject which is ok, but doesn't give you the same level of abstraction as SQLAlchemy (ActiverRecord-like ORM library) does
  • They want to switch to SQLAlchemy soon, cool!
Ruby/Rails tools that help
  • by Cloves Carneiro Jr.
  • a very good presentation
  • the speaker is a good example of a happy Rails programmer ;-)
  • I was already familiar with all of the tools, but I learnt some details
  • Capistrano
  • Rake
  • Subversion
  • autotest (zentest)
    • perfect integration with growl notifier, very cool!
  • rcov
  • TextMate

Grono.net talk
  • as far as I know Grono.net is the biggest Django application in the world
  • many performance challenges
  • "Share nothing" architecture
  • Lots of caching
  • The first version was in Java - "unmantainable", switched to Python
  • not many tests
  • experiments with WSGI tests
  • they use an old Django version, can't take advantage of new features (testing support)
RadiantCMS
  • a nice introduction to RadiantCMS
  • it has a good system of extensions
  • it's a Rails application, so you can easily extend it with your models/controllers
  • there is a wysiwyg plugin

Domain specific languages with Ruby by Jan Szumiec
  • Jan presented a very agile way of creating a valid DSL
  • The pair programming part (with Olle Jonsson) was very funny :)

Developing with IronPython and Windows Forms by us (Michael Foord and Andrzej Krzywda)
  • The talk was a little bit too long (90 minutes)
  • I enjoy coding live :)

Thursday, March 8, 2007

Andrzej on Test Driven Development

Test Driven Development helps me creating better software. TDD is not only about testing. It's more about designing and managing scope. I'll try to show what I mean by using a simple example based on tabbedimages application.

Tabbedimages is a simple image viewer. We are going to add drag and drop feature to it.
I start with a requirement, which I analyze and spike for a working solution. Based on the analysis I create a user story and an automated acceptance test. The acceptance test "drives" me when I'll add unit tests and the production code.
  • Requirement
    • tabbedimages is a simple image viewer.
    • We want to add a new feature to tabbedimages.
  • Title.
    • 'Drag&drop support'.
    • TIP: It's good to have a short title for a user story.
  • Analysis: let's list things to worry about:
    • Single image files
    • Multiple image files
    • Directories
    • Non-image files
    • Already open image.
  • Spiking
    • TIP: This part should give us better understanding of the problem
      • If we're sure how to implement the new feature we can skip this step.
    • tabbedimages is implemented using IronPython and Windows Forms.
      • The code is availalable here.
    • Google for 'drag and drop windowsforms' and see some code examples.
    • Check out the fresh version of tabbedimages.
    • Try to add the required feature to our code base (without tests).
    • Discover that Windows Forms has support for DragDropEffect.
      • Which displays a 'plus' sign if the thing that we're trying to drag is acceptable.
    • Add the DragDropEffect to the list of things to worry about.
  • User story:
    • Marten wants to drag and drop his images from his desktop to tabbedimages.
    • He starts tabbedimages.
    • He then drags the 'Faye001.jpg' file over the application.
    • The plus sign appears.
    • He drops it.
    • A new tab is created with a label saying 'Faye001.jpg'
    • 'She's cute' he thinks
    • Marten realizes that there are more Faye's pictures.
    • He drags 'Faye001.jpg' (again) and 'Faye002.jpg'.
    • He drops them.
    • Two new tabs are created.
    • He drags and drops readme.txt file.
    • The message box appears saying 'readme.txt doesn't appear to be a valid image file'
    • He quits tabbedimages.
  • Functional test:
    • Write the ideal code (DSL-like) that follows the user story steps:
    • marten.starts()
      he.asserts_number_of_tabs(0)
      he.drags('Faye001.jpg')
      assert shows_plus()
      he.drops('Faye001.jpg')
      he.asserts_number_of_tabs(1)
      he.asserts_tab_labels(['Faye001.jpg'])
      fayes_pictures = ['Faye001.jpg', 'Faye002.jpg']
      marten.drags_and_drops(fayes_pictures)
      marten.drops(fayes_pictures)
      he.asserts_number_of_tabs(3)
      he.asserts_tab_labels(['Faye001.jpg', Faye001.jpg', Faye002.jpg'])
      he.drags_and_drops('readme.txt')
      he.sees_message_box("readme.txt doesn't appear to be a valid image file")
      he.quits()
  • Implementation
    • Run the Functional Test (FT).
    • Whenever FT fails or you can think of any edge cases not covered by FT:
      • Write appropriate Unit Test that reflects the problem.
      • Add the implementation to fix the problem.
After the last phase the drag & drop feature should be ready to use. Of course, this example presented a very simple problem. Hopefully, the Test Driven Development as shown here should be easy to understand. In the near future, I'll try to write more about the implementation phase, including some refactoring techniques, so stay tuned!

Sunday, March 4, 2007

Doctests for showing snippets of code

From Public

  • djangosnippets.org shows many snippets of Python/Django code along with doctests.
  • Have a look at partitioning lists example.
  • This is what I think a good usage of doctests.
  • I'm not sure about using them *instead* of unittests.
  • Michael is also not sure
  • BTW, I find many of those Python code samples really pretty.
    • I couldn't find any with the string 'self' inside.
    • Maybe that's why I like them ;-)

def partition(thelist, n):
"""
Break a list into ``n`` pieces. The last list may be larger than the rest if
the list doesn't break cleanly. That is::

>>> l = range(10)

>>> partition(l, 2)
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]

>>> partition(l, 3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]

>>> partition(l, 4)
[[0, 1], [2, 3], [4, 5], [6, 7, 8, 9]]

>>> partition(l, 5)
[[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]]

"""
try:
n = int(n)
thelist = list(thelist)
except (ValueError, TypeError):
return [thelist]
p = len(thelist) / n
return [thelist[p*i:p*(i+1)] for i in range(n - 1)] + [thelist[p*(i+1):]]

Sunday, February 25, 2007

Writing Domain Specific Languages with Python

  • The process of writing DSL is the same in all languages.
    • You write an ideal spec.
    • Then, you try to fit the spec so that it's kind of valid syntax for the language you use.
  • An example of ideal domain language (well, maybe not so ideal, but quite good):

test '/Login':
GetReq(uname = 'a_user',
password = '')
expect_tmpl: 'login.tmpl'
expect_tmpl_has: 'errorMsg'
expect_tmpl_data: { 'uname':'a_user',
'missing_fields' : ['Password']}
expect_session_lacks: 'uid'
  • Now the same but as a valid Python syntax:

test_serv('/Login',
GetReq(uname = 'a_user',
password = ''),
expect_tmpl = 'login.tmpl',
expect_tmpl_has = 'errorMsg',
expect_tmpl_data = { 'uname':'a_user',
'missing_fields' : ['Password']},
expect_session_lacks = 'uid')

  • Useful tricks inclue:
    • keyword args (expect_tmpl_data=...)
    • unbounded arg lists (*args)
    • operator overloading __add__(self, other_form)
    • reflection via hasattr, getattr, vars and friends
  • Thanks to Dan Milstein for his talk at PyCon about Little Languages in Python.

IronPython and Windows Forms talk

The presentation in html version

TabbedImages application

  • We got some good feedback after the presentation.
  • To my surprise there were many people already playing with IronPython.
  • What's even more surprising, there were some people that have already used Windows Forms.
  • Watch the tabbedimages application, I'm going to extend it with some interesting features.
  • If you browse the sources for it, you'll find some simple tests in the MainTest.py.
  • They exist because I'm afraid I'm not able to not do Test Driven Development.

Saturday, February 24, 2007

PyCon 2007

The PyCon conference is really great.
So far I was at the following talks:
  • Internationalization
  • Parsing revisited
  • Developing desktop applications with Dabo
    • I didn't know that creating applications with wxPython is such a pain.
    • The Dabo core is simply a layer above the wxPython.
    • The application they used on the presentation was an image viewer.
    • The API was really nice and in many cases reminded me a simplified version of WinForms API (where you uses strings instead of overused in .Net enumerations).
    • wxPython (so Dabo as well) works on all important platforms (Windows, Linux, MacOS)
  • WSGI - an introduction
    • Because there are many different Python web frameworks, WSGI goal is to be able to integrate different layers of different applications.
    • I'm still not sure how it could be used in the Ruby world and if there is any need for that.
  • Writing parsers and compilers with PLY
    • Recently, I'm working quite a lot with parsers and PLY is the main tool we use.
    • It's very good, very efficient, very elegant
    • You declare your grammar rules using docstrings
  • Creating the WhatWhat project with TurboGears
    • WhatWhat is a very simple project tracking tool that was implemented in 4 days using TurboGears.
    • It seems that frameworks like TurboGears are becoming quite easy to learn and use.
    • Not sure if as easy as Ruby on Rails, though.
    • I'm still not convinced what could be the reason for me not to use Rails in web applications :)
  • pyweek: making games in 7 days
  • Creating games with Pygame on the GP2X
  • SQLAlchemy
    • Looks like a very powerful ORM tool for Python.
    • Reminds me of Hibernate.
    • There is a tool called Elixir which is a layer on top of SQLAlchemy and is more similar to the Ruby ActiveRecord.
    • Migrations are only available as extensions.
    • However, they are different than Ruby migrations. I didn't quite understand what is really the difference. I was told that Ruby migrations are dangerous because they do too many things for you (!?).
    • It seems to me the above is one of the fundamental differences between Python and Ruby programmers.
  • IronPython: Present and future
    • That was definitely the best talk so far, IMO.
    • Jim is doing a great work in Microsoft.
    • My name even appeared at his slide :-)
    • He wasn't able to read my name, though ;-)
    • What's so difficult with my name? - Andrzej Krzywda
    • There were screenshots of Michael's tutorial on IronPython and Windows Forms.
    • Seo was mentioned, of course. He is the author of FePY - the Comunnity Edition of IronPython.
    • The reason of this special edition is that MS can't easily apply patches that are sent by non-MS people.
    • IronPython is supported by the MS Robotics project.
    • Jim presented a small IP script where he used some MS Gaming API to create a simple 3d game.
    • There is a growing support for IronPython in ASP.NET
  • Embedding Little Languages in Python
    • Little Languages is what the rest of the world calls Domain Specific Languages.
    • The talk was about how to use Python to create such a DSL.
    • The idea is the same as always with DSL:
      • Firstly, you think of an ideal specification.
      • You try to use the language features to fit to the spec.
    • Python features that are helpful:
      • Keyword args
      • * args
      • getattr, hasattr
    • The speaker used _ convention in his code examples.
    • I also prefer it, but it doesn't seem to be so popular in the Python community (it is in Ruby community).