Archive for the ‘Work’ Category

How to write a ruby function that can accept either a string or a regular expression

July 30, 2008

While working on our unit test framework, I wanted to write a function that checks if a given Ext JS windoid is opened. The function should accept a title argument. This was easily implemented as:

	# title is a string with the exact windoid title
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
			i.class_name == 'x-window-header-text' \
			&& title == i.text \
		}
		return t && t.visible?
	end

I then realized that users might not have the full title of the window, and might only want to check for a windoid with a title that matches some regular expression. A simple and naive way to do it is

	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
			i.class_name == 'x-window-header-text' \
				&& (title.kind_of?(String) \
					? (title == i.text) \
					: (title =~ i.text)) \
		}
		return t && t.visible?
	end

However, I didn’t want to check in every loop iteration if the passed title argument is regular expression. Although performance is really a non-issue in our unit tests, it just seemed to be a cumbersome and ugly code. I thought of using the dynamic nature of ruby to add a method (e.g. “match?”) to both string and regular expression classes, so I can call it in a polymorphic way without having to detect the type of title:

	class String
		def match?(x)
			return self == x
		end
	end
	
	class Regexp
		def match?(x)
			return self.match(x)
		end
	end
	
	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
				i.class_name == 'x-window-header-text' \
				&& title.match?(i.text) \
		}
	return t && t.visible?

Before implementing it, I thought I’d check if a similar function already exists in Ruby’s Standard Library, and indeed, when I looked up the Ruby book for functions that are in both String and Regexp classes, I run into “===” (the Case Equality operator), which is meant exactly for this kind of things. Unlike JavaScript, where the === operator is meant for identity, in Ruby, the === method is usually called in case expressions to match the case target for each when clause. As said in the documentation, for the String class, === method tests for equality and is the same as ==, but for Regexp class, === method tests for matches, and is the same as =~. Exactly what I was looking for. So my little function can simply be implemented like:

	# title can be a string or a regular expression
	def windoidDisplayed?(title)
		spans = $browser.spans
		t = $browser.length && $browser.detect {|i| \
				i.class_name == 'x-window-header-text' \
				&& title === i.text \
		}
		return t && t.visible?
	end

BTW, while searching for that, I took a look at Watir‘s sources, hoping to learn from the experts as I’m new to Ruby.
I found that Watir source (watir.rb, lines 50-70) does a similar trick to what I initially thought to do – define a new “matches” method (and not “match?” like the ruby convention that is also used in Watir in methods like “exists?” and “visible?“), and don’t use the === method.
In addition, in several places in the code there are still explicit tests for String class, such as the verify_contains method in input_elements.rb and navigate_to_link_with_id and navigate_to_link_with_url methods in watir_simple.rb.

Handling browser on test start and end

July 23, 2008

When I started building a unit tests framework for our product, I decided that I want the browser to remain open through all tests, and only be closed at the end. This is because I don’t want a developer running these tests on his machine to be disturbed with many browser windows opened and closed.

However, as I quickly found out, there is no easy way to run code at the beginning and end of a test suite. This seems to be intentional, as each test case should be separate and independent of other tests in the suite. The setup and teardown methods are run for each test case.

One simple solution was to drop it and simply start the browser on every test case and close it on end, but when I played with it a little more, I reached an even better solution. I’m starting the browser at the beginning of the test and assign it to a global $browser. Then, if the test fail (which can be detected by @test_passed), we set $browser to nil, so the browser window remains, and subsequent tests create and use a new browser window. If the test succeed, the browser window is reused through $browser. At the end of the test, we close the browser if $browser is not nil. With this constellation, after the test suite finishes running, we have a browser open for each failed test, so we can inspect what went wrong and how the application looks at the time of the fail.

Here is the code I used to implement it:

# cleanup on close
END { # close browser at completion of the tests
$browser.close if $browser && $browser.exists? && !ENV[‘DONT_CLOSE_BROWSER_AT_END’]
Watir::IE.quit
}

module MyTestSuite < Watir::TestCase ############################################################# # setup method: # This setup function is called by the UnitTest framework before any test is run. # The function initialize the global $browser object if needed ############################################################# def setup return if $browser $browser = Watir::IE.new $browser.speed = :fast $browser.add_checker(PageCheckers::NAVIGATION_CHECKER) $browser.maximize unless ENV['DO_NOT_MAXIMIZE_BROWSER'] end ############################################################# # teardown method: # This teardown function is called by the UnitTest framework after any test is run. # The function reset the global $browser object in case an error # occurred (unless NO_NEW_BROWSER_ON_ERROR), so # a new browser window will be created in case an error occurred. ############################################################# def teardown $browser = nil unless @test_passed || ENV['NO_NEW_BROWSER_ON_ERROR'] end # ... test cases methods goes here end [/sourcecode]

Watir – first impressions

June 24, 2008

Our product doesn’t have unit tests. There, I said it. For a long time I’ve been looking for a way to build unit tests. There are three factors that prevented me to do so until now:

  1. It seems not to be really needed. When testing is needed, developers go over an installation and check that everything is working. Building a suite of tests to test all possible scenarios seems to be a huge task that doesn’t worth the effort.
  2. This is a web application that is constantly changing, so it seems hard to write robust test that will not break on every change. In addition, it uses a session ID that is passed in the URL, so URLs are different on every run.
  3. The data keeps changing. Our data is on the mainframe, and it is owned and used by other teams so we can’t expect to have the same data all the time, which makes it problematic to build solid tests.

In the past I tried using Selenium, but never really fell in love with it, and never got the time to deeply look into it and test it.
Recently I saw Ofer’s post about Watir and decided to take a better look at it and see if we can use it. Since I’m fluent at Javascript, and had a recent experience with Perl working on an installation automation project for a few months, catching Ruby was quite easy. As Ofer observed, Ruby seems to be a really nice language, but I have some problems adapting to Watir. For example, it took me sometime to understand how Javascript code can be invoked from Watir, but now I know it is done using:

$browser.ie.Document.parentWindow.execScript(js_expression)

At first, since I come from normal OO languages, I thought of creating a base ProductUnitTest class that derives from Watir::TestCase, and then, for each version or fix pack, we will create a VersionXXUnitTest class that will derive from ProductUnitTest. However, this doesn’t work well, since Test::Unit looks for all the included classes that derived from Test::Unit::TestCase, and since we would have to require ProductUnitTest in order to derived from it, Test::Unit will see two such class and will run the tests in ProductUnitTest twice – once for ProductUnitTest, and once for the derived VersionXXUnitTest.

Checking further, I came to the idea I have to use Ruby’s mixins, which is somewhat similar to Java’s interfaces, but more comfortable and easy to use, since Ruby is a dynamic language. So now, I define all basic tests and helper methods (like setup and teardown – see next post for more details on this) in a module named ProductUnitTest, and then, for each version, I define a VersionXXUnitTest class that derive from Watir::TestCase, and then includes ProductUnitTest so all the tests (and helper methods) are there, but only invoked once, for VersionXXUnitTest.

To conclude, Watir seems like a very nice framework to test our web application, and this will probably solve problem #2 above. I plan to broaden its use when we start building our next fix pack, where we have specific things we need to fix, and whenever we fix something, we should write a unit test for it right away. This is easier then writing and maintaining unit tests for an entirely new version that keeps changing. I hope that once our developers get used to it and see how it can help integration and acceptance tests they will see its value and overcome problem #1 above. I still need to find a good way to bypass problem #3.

I already showed what I did to Amit from QA, and she really liked it and said it might be useful for them as well.

P.S.
Another nice benefit of learning Ruby is that now I better understand some of the design made by the Prototype team members, which were clearly thinking in Ruby, as they are using Ruby on Rails.

VBA Macro Arguments

April 26, 2007

After we started to use Remedy for our customer support, I got annoyed by the way Remedy displays the history of customer cases (Remedy calls them issues), so I wrote a VBA script in Microsoft Word that takes the history generated in Remedy and displays it in a readable format (let me know if you’re interested in this script – it is really great if you are working with Remedy to support customers)

Since I intended this macro to be used by others, and not only by me, I tried to make its installation as simple as possible. I had no choice but asking my users to open the VBA editor and import my Remedy.bas module (I really didn’t want to go into developing a Word add-in with an installation program), and then users would have to add a toolbar button calling the macro. Since this was quite a complex script, it had many internal functions which I didn’t want users to call from a toolbar button – I only wanted them to add one or two buttons that called some of the public functions. To make sure they only use the public functions, I prefixed all the internal functions names with “RAH_” (RAH stands for Remedy Activity History), so they’ll know they shouldn’t look at macros with that name.

Later, I added a new procedure (“Sub” as it is called in VBA) that do some functionality. This function could be called from within other macros, in which case a document argument can be passed, or it could be called from a toolbar button, in which case the active document should be used. I noticed, however, that the Customize dialog didn’t show any macro function or procedure that has an argument, even though this argument was optional, and it was valid to call the function without arguments. This was annoying at first, and to bypass it, I had to create another proxy procedure that simply calls the internal procedure without any parameters, and only this proxy procedure could be added as the action for a new toolbar button.

However, later I realized that this feature of the Customize dialog can be put to good use and solve the internal procedures problem I had – I simply added an optional dummy argument to any internal procedure that didn’t have any argument. On one hand, it didn’t change the meaning of my code, and any code that called this internal procedure didn’t have to change. On the other hand, all these internal procedures don’t appear in the Customize dialog any more, since they now have an argument.

Update: This trick of hiding the internal procedures has one problem. My script uses the Application.OnTime procedure when the Remedy application haven’t finished opening the history window to make the script sleep and then check again one second later. It appears that the Application.OnTime procedure behaves like the Customize dialog, and is only willing to accept a procedure without arguments in its Name argument. If the procedure specified in the Name argument has at least one argument, the OnTime procedure won’t run it. So the macro has to have at least one internal function with no arguments.

One cumbersome way to solve it is to set some global flag before calling Application.OnTime, and then make Application.OnTime call a public procedure which will first check for that flag and if it is set, call the internal procedure and exit, but I’m not sure that the goal of hiding this one internal function worth the effort of making the code less readable.

Update 2: Thinking about it some more, I came to the conclusion that the dummy argument is a nice trick, but is not really needed. Setting the internal procedures as Private also hides them from the Customize dialog. How didn’t I think about it before…?

This, however, doesn’t solve the Application.OnTime problem, as the OnTime procedure cannot call Private procedures, so the procedure it calls must be public with no arguments, so it will appear in the Customize dialog.

Update 3: I found another reason for having non-private functions. My macro tries to hide unimportant parts of the case history, such as “original message” parts and confidentiality signatures. When doing so, it adds a link that when clicked, shows the hidden text. This is done by adding a MACROBUTTON field that calls the appropriate macro. It appears that this macro must also obey the rules of the Customize dialog for the field double-click to work – it has to be non-private, and with no arguments.

Activity Logger

June 11, 2006

I've pretty much neglected this blog recently. The usual excuse is lack of time, but this time I also have the exotic excuse of a business trip to Stockholm.

I've decided I should really write more here, especially since our company's internal blog, which is based on Roller just got almost unusable when a new version was installed, and I don't know who is directly in charge of it. So I'll blog here about my work revelation, using names initial to avoid unwanted exposure.

So, here goes:

In our work, we are required to report the time we spend working. Usually, when I come with the transportation, this is not a problem, since then I know I worked for 10 hours. However, sometime I work form home, or the transportation leaves early, and I don't always recall how much exactly I worked. I tried to look for tools to log my activity, but failed to find something useful.

I thought of writing a tool to do it, but didn't know how to detect that the screen is turned off, as a sign of me going home (I don't use screensaver), or how to detect idle time in which the computer is not used. Then it suddenly hit me. I can simply record the last mouse position, and constantly check if it has changed. If it has, it's a sign I'm working. If not, well, I probably left m computer, but to be sure, I check that for 5 minutes, to make sure I'm not just staring at some line of code while debugging.

That looks really promising, though I still need to finalize the best and easiest to use format of the output log file.

Note: after looking at that JoS discussion again, I saw that MarkTAW recommended Allnetic Working Time Tracker, which I never really checked, so I'll give it a try as well.

Another note: Once I'll get a laptop to work with, this will be much less of a problem, since the time the laptop is running will usually be the times I'm working. Also, once we move to global salary, it will be less important how much time I exactly work.