Apr 03

I18n label is still not supported in rails 2.3.2. Here is the patch to make it work. However, I don’t want to change the source code of rails. Hence, I create a monkey patch.

module ActionView
  module Helpers
    class InstanceTag
      def to_label_tag_with_i18n(text = nil, options = {})
        text ||= object.class.human_attribute_name(method_name) if object.class.respond_to?(:human_attribute_name)

        to_label_tag_without_i18n(text, options)
      end

      alias_method_chain :to_label_tag, :i18n
  end
end
end

Load the file in the environment.rb or any file in confit/initializers will make it work.

Feb 11

ORDER BY items must appear in the select list if SELECT DISTINCT is specified.

Add the following code at the end of method add_limit_offset!(sql, options) in sqlserver_adapter.rb

if options[:order]
order_fields = options[:order].split(',').collect do |field|
column_name = field.split(" ")[0]
"#{column_name} AS #{column_name.gsub('.', '_')}"
end
sql.gsub!(/^\s*SELECT(\s+DISTINCT)(.*?) FROM/i, "SELECT\\1 \\2, #{order_fields.join(', ')} FROM")
end
Feb 06

In rspec, I usually need to define a matcher to match arguments. I don’t want to create a match for every should_receive method. I just want to assert the arguments I care. I try to define a code block to assert my call arguments. I google it for a while and could not find an answer.

I want to do something like:


User.should_receive(:find).with(assert_that(lambda { |args
  options = args.pop
  options[:include].should == expected_include
  options[:limit].should == 100
}).and_return(:users)

After reading rspec source code, I finger out rspec should_receive argument allow code block. That’s easier than what I expect. The finial code is:


User.should_receive(:find).with do |*args|
  options = args.pop
  options[:include].should == expected_include
  options[:limit].should == 100
  true
end.and_return(:users)

remember to return true at the end if all assertions pass.

Jan 17

The current will_paginate plugin doesn’t support localization. Adding the following code into the application_helper will enable i18n for will_paginate.


include WillPaginate::ViewHelpers

def will_paginate_with_i18n(collection, options = {})
will_paginate_without_i18n(collection, options.merge(:previous_label => I18n.t(:previous), :next_label => I18n.t(:next)))
end

alias_method_chain :will_paginate, :i18n

And add ‘next’ and ‘previous’ attribute in the locales file.

for example zh.yml under RAILS_ROOT/config/locales

zh:
previous: '前一页'
next: '下一页'
Dec 30

SEO has become a hot topic in the Internet.  I am working in my personal project which I’d like to have a friendly URL. I need to create a slug as my friend URL. But, the slug is case sensitive in my case. for example, word ‘fisher’ is different to name ‘Fisher’ in a dictionary. unfortunately, MySQL is case sensitive by default. So I need to change the setting for the slug field in my table.

I change my slug field in MySQL by adding the following code into my migration script:


execute %{ALTER TABLE TABLE_NAME MODIFY slug varchar(255) COLLATE utf8_bin NOT NULL}

That works fine in my production database which is MySQL. However, I am using sqlite3 in test which is case sensitive by default. So I need to modify the field only in MySQL database. The finial code I put in my migration script is following:


if ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] == 'mysql'
execute %{ALTER TABLE words MODIFY slug varchar(255) COLLATE utf8_bin NOT NULL}
end

COLLATE options in MySQL:

  • utf8_bin: compare strings by the binary value of each character
  • utf8_general_ci: compare strings using general language rules, case insensitive
  • utf8_general_cs: compare strings using general language case sensitive
Nov 27

50 days ago, I’ve launched my personal website. I spend 2 months of my spare time to build this web application based on ruby on rails. There’re 5 tools available at the moment.

1. RSS Feed Icon Generator

2. Javascript Optimizer / Optimiser

3. Ajax Activity Indicator Generator

4. Css Optimizer / Optimiser

5. HTML and CSS Rounded Corner Button Generator

May 09

Firebug - Firebug integrates with Firefox to put a wealth of web development tools at your fingertips while you browse. You can edit, debug, and monitor CSS, HTML, and JavaScript live in any web page.

Web Developer - The Web Developer extension adds a menu and a toolbar to the browser with various web developer tools.

FireShot - FireShot is a Firefox extension that creates screenshots of web pages.

ColorZilla - Advanced Eyedropper, ColorPicker, Page Zoomer and other colorful goodies…

YSlow - YSlow analyzes web pages and tells you why they’re slow based on the rules for high performance web sites. YSlow is a Firefox add-on integrated with the popular Firebug web development tool.

Mar 11

In this post, I will talk about creating nicer syntax for verifications.

In Ruby, we can add new methods into existing class. Rspec adds some nice assert methods such as should_equal(expected_string) into string class. However, it is impossible to do it in Java. Right, we can’t do it in unit test. Fortunately, we are writing automated tests. we can control what to return. Hence, I write a wrap class to allow code to use rspec-like syntax.


public class TextDSL extends Assert {
private String text;
private String errorMessage;

public TextDSL(String text) {
this(text, "");
}

public TextDSL(String text, String errorMessage) {
this.text = text;
this.errorMessage = errorMessage;
}

public void shouldEqual(String expectedText) {
assertEquals(errorMessage, expectedText, text);
}

public void shouldNotEqual(String notExpectedText) {
assertFalse(errorMessage + " not expected:<" + notExpectedText + "> but was:<" + text + ">", notExpectedText.equals(text));
}

public void shouldEqual(int expectedInteger) {
assertEquals(errorMessage, String.valueOf(expectedInteger), text);
}

public void shouldNotEqual(int notExpectedInteger) {
assertFalse(errorMessage + " not expected:<" + notExpectedInteger + "> but was:<" + text + ">", String.valueOf(
notExpectedInteger).equals(text));
}

public void shouldInclude(String expectedIncludedText) {
assertTrue(errorMessage + " expected included:<" + expectedIncludedText + "> but was not included in:<" + text + ">",
text.contains(expectedIncludedText));
}

public void shouldNotInclude(String expectedNotIncludedText) {
assertFalse(errorMessage + " not expected included:<" + expectedNotIncludedText + "> but was included in:<" + text + ">",
text.contains(expectedNotIncludedText));
}
}

then we can get nicer syntax to express our intent for verification.

dropDownList.shouldDisplayLabel -> dropDownList.lable().shouldEqual(expectedLabel)

dropDownList.shouldDisplayIndex -> dropDownList.index().shouldEqual(expectedIndex)

and I can do something like verify css style includes a particular style:

dropDownList.css().shouldInclude(”errorField”)

We can add more methods to TextDSL class. or we can create other DSL class we want to verify the object we want.

Later on, I will post the source code so that you can see the whole implementation.

Feb 27

I was working in a project which used Selenium RC as web test tool. The functional test coverage is high. However, as the application has become more complicated and more tests have been added, the automated testing becomes a bottleneck for velocity. The problems?

  • Hard to understand the intent of automated tests
  • Duplication of Selenium test code
  • Hard to reuse
  • Page objects has all responsibilities of page operations and verifications
  • Test the wrong thing
  • Because of complexity, fewer scenarios are created
  • Overtesting cause performance problem
  • New team members find it hard to write tests
  • QAs take a long time to write automated tests

Let’s see how to solve all the problems listed above.

In the project, developers and QAs are both writing selenium tests. Without navigator into the code, it’s very hard to understand what does the particular test do.One way to solve the problem is to name the test better. for example testShouldAllTestFieldSetMaxLengthAsWhatTheyExpected(). However, selenium tests is not unit test, sometime it’s not easy to give a name to define all the operations and verification of the tests.

Recently, DSL became a hot topic. Define DSL in automated tests would help develops and QAs (even BAs) understanding the intent of all tests. Hence, I decide to define DSL for elements in the page object.

The advantage to use DSL as following:

  • Easier to read
  • Precise error message, i.e.:
    • assertTrue(selenium.isElementPresent(”id”) will give you message”expect true, but false”, what the heck does that mean when you see the error message?
    • element.shouldExist() will give you message “expect element ### exist, but not exist”, you can customized the error message you want.
  • Reuse DSL
  • No more asserts in the test class
  • Quicker development
  • Precise testing

Here I show some simple DSL. (two types: operations and verifications)

TextBox:

  • type(String text)
  • shouldDisplayRedBorder(), shouldNotDisplayRedBoder()
  • shouldDisplayValue(String expectValue)
  • shouldBeReadOnly(), shouldNotBeReadOnly()
  • shouldBeDisabled(), shouldNotBeDisabled()
  • shouldExist(), shouldNotExist()
  • ……

Radio:

  • click()
  • shouldBeSelected(), shouldNotBeSelected()
  • ……

DropDownList:

  • selectLabel(String label)
  • sleectIndex(String index)
  • shouldDisplayLabel(String expectedLabel)
  • shouldDisplayIndex(int index)

Checkbox, Label, ErrorIcon, Table…

Depends on the project, you can create more customized element suitable for your need.

In page objects, we just need to return element object. for example:


class AmazonPage

public searchInDropDownList() { return newDropDownList("url"); }

public fieldKeywordTextbox() { return newTextbox("field-keyword"); }

public searchButton() { return newButton("go"); }

}

then the test become look like


amazonPage.searchInDropDownList().selectLabel("Books");

amazonPage.fieldKeywordTextbox().type("Selenium");

amazonPage.searchButton().click()

self-documentation is better than having a lot of comment in the test.

In selenium DSL II, I will talk about how to define better DSL syntax.

Jan 07

When element doesn’t contain any id and name, using xpath to find the element is very important for writing selenium test. I list some sample locate for the element which doesn’t contain id or name.

1. get the link with the link text<a href=”link url”>Link Text</a> -> link=Link Text

2. get element with the element text

<a href=”link url”>Link Text</a> -> //a[text()='Link Text']

3. get element with part of the element text

<a href=”link url”>Link Text</a> -> //a[contains(text(), 'ink Tex')]

4. get element with an attribute

<a href=”link url”>Link Text</a> -> //a[@href='link url']

5. get element with two attributes

<input type=”text” value=”value”/> -> //input[@type='text' and @value='value']

XPather is a firefox plugin help you to find the xpath.

There is more element locators document to be found on Selenium Doc