Tuesday, September 30, 2008

Testing Dynamic Web Applications

At RailsConf Europe, in the Q & A portion of my talk on Functional Testing someone asked what I recommend for testing Javascript.

Ugh. Testing Javascript. Is it possible to recommend something when everything you've seen is terrible? Usually I'm cool with picking the tool that sucks the least, but when it comes to Javascript testing the only words that come to mind are: epic fail.

In the past I've failed in two different ways.My response in Berlin was "The pivotal guys are smart, so if I were going to try something new it would be Screw Unit." Of course, I just googled and found the RubyForge project and two with the same name on GitHub. I'm sure one of those is the current trunk.

A few days later it occurred to me what the correct answer was -- Don't use Javascript and you won't have to test it.

No, I'm not suggesting that we should all go back to mostly static websites. Static content is fine for some things, but GMail is an obvious example of a site that is better done dynamically.

However, Javascript isn't your only choice for highly dynamic websites.

These days, if I were writing a website that required any dynamic interaction I would absolutely use Flex or Silverlight. I've done Flex, and it was nice to work with, but I must admit that I'm lured to Silverlight because it's going to (or does already?) support Ruby.

I'm not sure what the Silverlight testing story is, but I found Flex (and ActionScript) to be quite testable. The single biggest win (as I've said before) is that I no longer need to do in-browser testing. Removing the browser from the equation is huge. No more IE bugs causing your tests to fail, no long start up times as the browser is run, etc.

Testing with FlexUnit (with it's drawbacks) is an order of magnitude better than any experience I've had testing Javascript.

I'm comfortable saying that I would still use Javascript for trivial features that provided so little business value that they did not warrant testing. However, any features that provide noticeable business value must be tested, and I would move to a RIA solution instead.

In my experience, the benefits of switching to a RIA solution are dramatic, one of the largest being: you no longer need to worry about testing Javascript.

Labels: , , , ,




Tuesday, July 08, 2008

ActionScript: Calling a function by name

In Ruby I can call a method using it's name (as a string) using send.

def hello
"hello world"
end

send "hello" # => "hello world"

This type of behavior is helpful in scenarios where I store method names as values and execute those methods based on user actions.

In Flex I can store Objects in Views, so I generally don't need this capability. However, I recently wanted to execute a function based on what MenuItem was clicked. The menu was composed of MenuItems that were defined by XML. I couldn't figure out how to get a function into the XML, so i ended up putting the function name as an attribute of each MenuItem. From there, the only trick was figuring out how to call a function on an object if you have the name of the function stored as a string.

It turns out, it's very easy, and exactly what you do in Javascript. All you have to do is pass the string in brackets to the object that has the function defined.

this["methodName"]();

If you're interested in the context, the code below is similar to what my application required.

<mx:MenuBar id="myMenuBar" labelField="@label" itemClick="menuHandler(event);">
<mx:XMLList>
<menuitem label="Submit" handler="submit"/>
<menuitem label="Reset" handler="reset"/>
</mx:XMLList>
</mx:MenuBar>

private function menuHandler(event:MenuEvent):void {
this[event.item.@handler](event);
}

private function submit(event:MenuEvent):void {
// do submit logic
}

public function reset(event:MenuEvent):void {
// do reset logic
}

Labels: ,




Tuesday, June 24, 2008

Flex: Expert Developers Needed

The majority of my Flex posts have been pro-Flex. The two biggest reasons I advocate for Flex are:There are a few places that I'd love experts to help evolve.Unfortunately, most of the developers I talk to these days are not excited about Flex adoption. I'll be the first to tell you it's not perfect, but I'm surprised by the hesitation level. I don't consider myself an early adopter, and I don't tolerate much pain from any technology. But, I don't think Flex fits in the category of bleeding edge or excessive yak shaving. There are gotchas, but what technology doesn't have them? The most depressing aspect of the expert hesitation is that they wont get the opportunity to help shape the language/platform.

I already find myself so effective using Flex that I would reach for it anytime I consider Javascript in the future. But, the world would be a much better place if the experts continued to improve upon an already solid base.

Labels:




Monday, June 23, 2008

Flex: Anonymous Classes as Test Stubs

In my previous post, The State of Flex Testing, I stated that I use anonymous objects as stubs. This entry will show how to easily create stubs that stub methods and properties.

If you simply need a stub that has a property, the following code is all you'll need.

{state: "ready"}

The above code creates an object that response with "ready" to the message state. Adding methods is also easy if you remember that you can create properties that store anonymous functions (just like Javascript). If you need the execute method to return true, the following stub should do the trick.

{state: "ready", execute: function(){ return true }}

That's really it. You can define methods and properties on anonymous objects as you please.

Is it beautiful? Not really. I've considered a few other options would allow you to define methods without the anonymous function, but it's never been so annoying that it was worth spending the time to get the implementation correct. I suspect anyone who put their mind to it could come up with a nice implementation in a short amount of time.

Labels: , ,




Flex: The State of Testing

Given my focus on testing, it's not surprising that the most common question I get about Flex is: Is it testable? Of course, the answer is yes otherwise I wouldn't be using it.

Out of the box, it's very similar to early JUnit. Like all XUnit ports, it's based on creating testing classes and defining methods that are tests. There's setup, teardown, and all the usual features you expect from XUnit ports.

There's a FlexUnit swc (library) that assists in creating a test swf file, which runs all the tests in the browser. There's a few blog entries that give details on how to generate an XML file from the test swf, so breaking a build or reporting results is possible. I haven't bothered to go that far, but others have successfully blazed that trail.

In general, testing Flex is not great, but I rarely find myself concerned or unhappy. Here's a few reasons why.If you only want a high level opinion -- it's good enough, but not ideal or ground breaking.

I'm not a fan of manually adding tests to my test suite, but just like Paul Gross, we dynamically create ours. So it's not a problem. We didn't stop there though, we also generate the package and class name; therefore, our test classes stay pretty clean. The code below is an example of an entire test file (the ones that we work with).

public function testLimitSortOrderCriteriaXml():void {
var smartListCriteria:* = new SmartListCriteria();
smartListCriteria.addLimitCriteria({order:"an order"});
assertEquals("an order", smartListCriteria.toXml().sort.order);
}

public function testLimitSortFieldCriteriaXml():void {
var smartListCriteria:* = new SmartListCriteria();
smartListCriteria.addLimitCriteria({field:"a field"});
assertEquals("a field", smartListCriteria.toXml().sort.field);
}

Those methods wouldn't do much in isolation, but if you generate the surrounding (tedious) code, they work quite well. Here's the rake task that I use.

desc "Generate test files"
task :generate_tests do
tests = Dir[File.dirname(__FILE__) + "/../../test/flex/tests/**/*Test.as"].each do |file_path|
file_path = File.expand_path(file_path)
package = File.basename(File.dirname(file_path))
emit_file_path = (File.dirname(file_path) + "/" + File.basename(file_path, ".as") + "Emit.as").gsub(/tests/,"generated_tests")
File.open(emit_file_path, "w") do |file|
file << <<-eot
package generated_tests.#{package} {

import flexunit.framework.TestCase;
import flexunit.framework.TestSuite;
import uk.co.company.utils.*;
import uk.co.company.smartLists.*;
import mx.controls.*;

public class #{File.basename(emit_file_path,".as")} extends TestCase {

#{File.readlines(file_path)}
}
}
eot

end
end
end

As you can see, I generate test files and which are later used as source for the test swf. It's a few extra steps, but I never see those steps, I just write my test methods and run rake test:flex when I want to execute the tests. The one catch is that I can't run individual tests, which is terrible.

There are no mocking frameworks that I've seen, so that's kind of a bummer. Though, in practice I haven't found myself reaching for a mock anyway, but that will probably depend on your style. Again, I don't have much behavior in my views, so I don't need rich testing tools. Of course, I'd love to have them if they were available, but I don't find their loss to be a deal breaker. I do reach for stubs fairly often, but anonymous classes seem to work fine for that scenario.

There is one huge win that I'll conclude with. When using HTML & Javascript you have to test in browser to ensure that it works. This isn't true of Flex since there aren't browser issues. So, I can test my application using FlexUnit, and be confident that it just works. Removing the need for a (often slow and fragile) Selenium suite is almost enough to make me ditch HTML & Javascript forever.

Labels: ,




Tuesday, June 10, 2008

Flex: Objects in Views

Imagine the following requirement:
The application needs a combo box that has the following options:
  • delete
  • mark as spam
  • bounce
When delete is selected the current email (the one being viewed) needs to be moved from the inbox to the trash.

When mark as spam is selected the current email should be tagged as spam.

When bounce is selected the server should send a reject message to the server where the email originated.
Assume you are using the web. The traditional approach is to create a drop down with unique values that can be used on the server side (generally as a case statement). The unique values are strings, which represent keys, which are used to determine what the correct course of action is. This scenario works, and is familiar to most web developers, but it's not the most elegant solution.

Flex offers you a better solution. The Flex ComboBox allows you to set the dataProvider to an array of anonymous objects which can contain pretty much whatever you need. Even if you don't know Flex and ActionScript, the following code should still be readable, and interesting.

function init() {
comboBox.dataProvider = [{label: "delete", command:DeleteCommand}, {label: "mark as spam", command:MarkAsSpamCommand}, {label: "bounce", command:BounceCommand}];
comboBox.addEventListener("change", function(){ new comboBox.selectedItem.command().execute(); });
}


function init() {
comboBox.dataProvider = [{label: "delete", execute:delete}, {label: "mark as spam", execute:markAsSpam}, {label: "bounce", execute:bounce}];
comboBox.addEventListener("change", function(){ comboBox.selectedItem.execute(); });
}

function delete() {
// delete implementation
}

function markAsSpam() {
// mark as spam implementation
}

function bounce() {
// bounce implementation
}

This is one of the things I really like about Flex. Since I'm not tied to HTML and strings, I can put whatever I like in the view. The first example keeps all the logic out of the view and simply creates a new command and executes it immediately. It's nice and clean without worrying about converting from strings to classes. The second solution relies on functions to handle processing. In this case you could be doing something as simple as showing or hiding components in the view (not something you'd need a class for).

Either way, I'm working with first class concepts, instead of string representations of first class concepts. The result is cleaner code that's easier to work with.

Labels: ,




This page is powered by Blogger. Isn't yours?