Team Considerations in Choosing Automated Test Scripting Language, Why Ruby is much better than JavaScript?
Compare two languages for automated testers, manual testers, business analysts, customers, managers, and developers.
Comparing programming languages often lead to heated debates among programmers (in different teams), with no winners. There are no such debates among the developers within a team, as they all use the same one.
Automated end-to-end test scripts via UI drive the product (e.g. web app in Chrome browser), this means all team members can utilize the script. This article will compare two scripting languages (Ruby vs JavaScript) from a within-the-team perspective.
Note: please note the context of this article, that is, all of a software team members, including programmers, automated testers, manual testers, business analysts, and even customers run/edit/read automated end-to-end (via UI) tests, like in a real Agile team. Specicically, the automated test count is > 50 (Level 2 of AgileWay Continuous Testing Grading) and are run daily as regression testing. With real automatied end-to-end regression testing, the software is technically deployable every week or two.
Fake test automation engineers who are pretending test automation with Cypress or Playwright will disagreee. Again, this is beyond the individual tester, is about th team. To give you a perspective, I showed Visual Code and TestWise to several business analysts and manual tests to view/run automated tests (in an objective manner, see this), every single of them selected TestWise.
I consider myself quite open-minded in terms of programming languages (for test automation), authored Selenium WebDriver Recipes in all of Selenium’s five official languages: Ruby, Java, Python, JavaScript, and C#.
The two languages I selected to compare are Ruby and JavaScript. I excluded the two compiled languages: Java and C#, as Automated Test Scripts Shall be in the Syntax of a Scripting Language, Naturally!; Python is not a bad choice (better than JS) for scripting automated tests, however, its strict indenting is not user-friendly to non-python programmers. We are talking about the scripting language for all members of a software team.
Table of Contents:
· Easy to read
∘ 1. Unless conditional
∘ 2. Use a question mark in the function name
∘ 3. No need to use `()` for a function
· Intuitive
∘ 4. Local caching files such as node_modules
∘ 5. Intuitiveness of Syntax
∘ 6. “More than one way of doing something”
· Concise
· Flexible
· Easy to Set up
· A Testimony
Easy to read
Readability is an important attribute in automated test scripts (more than code), as other team members (in real agile projects), such as business analysts and manual testers, will also use them.
1. Unless conditional
Every programming language has if
condition statements, and if (!condition)
for negative cases.
Ruby:
In my Selenium tests, I keep the browser open if running in the ‘debug’ mode.
after(:all) do
if !debugging?
@driver.quit
end
end
The version below is better, using unless
:
after(:all) do
@driver.quit unless debugging?
end
JavaScript: No unless
if (!condition) {
// ...
}
Some JS programmers may argue there is a way, as below.
let unless = (condition,callback)=>{!condition && callback();}
let a = 10;
let b = 20;
unless(a > b,()=>{console.log("A should be greater than B")})
However, it is not very good, isn’t it?
2. Use a question mark in the function name
I (and many business analysts/manual testers) find having ?
in functional names making test scripts easy to read, and conveying the meaning better.
Ruby:
def is_debugging?()
# ...
end
Usage:
after(:all) do
@driver.quit unless debugging?
end
JavaScript: Not allowed.
function isEmpty()? {
^Uncaught SyntaxError: Unexpected token '?'
3. No need to use `()
` for a function
For programmers, taking function_name()
syntax for granted when using a function. However, it is NOT the case for non-technical staff.
Ruby:
passenger_page = PassengerPage.new(driver)
passenger_page.click_next
try_for(3){ expect(page_text).to include("Must provide last name") }
JavaScript:
let passenger_page = new PassengerPage(driver)
await passenger_page.clickNext();
await driver.findElement(By.tagName("body")).getText().then(
function(the_page_text) {
assert(the_page_text.includes("Must provide last name"))
}
);
If I take the ()
out,
The test failed, but that’s OK if there is a clear indicator of where the failure is. However, it is not the case, the failed line is not .clickNext
but the next one. The output:
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert(the_page_text.includes("Must provide last name"))
This would cause confusion, right?
Intuitive
4. Local caching files such as node_modules
Ruby: Not at all.
Some languages, such as Python, generate runtime caches as well, .pytest_cache/v/cache
.
In Ruby, you don’t need to worry about any of these at all.
Node.js: node_modules
hell
Often a team member accidentally check-in the large .node_modules
folder (which is very large), and bloats the Git repository. JS programmers might take this for granted, however, Ruby engineers found this totally unnecessary and insane.
Apparently, even some JS programmers don’t like node_modules
either, created the above joke image.
5. Intuitiveness of Syntax
If the test script syntax is not intuitive, it surely increases the maintenance effort. Let’s see two versions of the same test case in Ruby and JavaScript. Both scripts follow the Maintainable Automated Test Design.
Ruby: Concise and Very Intuitive
driver.get('http://travel.agileway.net');
login("agileway", "testwise") # helper included
flight_page = FlightPage.new(driver)
flight_page.select_trip_type("oneway")
flight_page.select_depart_from("Sydney")
flight_page.select_arrive_at("New York")
flight_page.select_depart_day("02")
flight_page.select_depart_month("November 2021")
flight_page.click_continue
expect(page_text).to include("2021-11-02 Sydney to New York")
JavaScript: Lengthy and Not Intuitive
await driver.get('http://travel.agileway.net');
await helper.login(driver, "agileway", "testwise");
let flight_page = new FlightPage(driver);
await flight_page.selectTripType("oneway")
await flight_page.selectDepartFrom("New York")
await flight_page.selectArriveAt("Sydney")
await flight_page.selectDepartDay("02")
await flight_page.selectDepartMonth("November 2021")
await flight_page.clickContinue()await driver.findElement(By.tagName("body")).getText().then(
function(the_page_text) {
assert(the_page_text.includes("2021-11-02 New York to Sydney"))
});
I will just point out the key differences, for the issues with JavaScript syntax, please read Why JavaScript Is Not a Suitable Language for Real Web Test Automation?
await
Confusing promise,
.then( ... )
})
Also, on using the login
function defined in the helper, we can just use it directly in the test script, thanks to Ruby’s module (which is great, using it well and being happy).
6. “More than one way of doing something”
Software Engineers might be aware of this:
Ruby: “There is more than one way to do it.”
Python: “There should be one — and preferably only one — obvious way to
do it.”
Here, Python is better than Ruby in the context of scripting automated tests. Why? because other team members are more likely to produce the same-style scripts, i.e., easier to maintain for a team.
How about JavaScript? It has many more ways and often vastly different ways than Ruby.
“In JavaScript there is not really a concept of true classes, at least not in the way people from truly Object Oriented languages would expect them to be. Instead we have a the prototype chain, and at least 4 different ways of creating objects with shared methods and behaviour. This is super confusing, so I really like that Ruby just offers the one way to do it!” — Harriet
In other words, JavaScript is a worse choice for test automation. The article “Why JavaScript Is Not a Suitable Language for Real Web Test Automation?” listed some examples.
One specific example is that I have been using selenium-webdriver/testing
since Selenium 3. With that, I could avoid using await
. However, this was removed in Selenium 4. I had to update all my recipe tests in Selenium WebDriver Recipes in Node.js and sample tests for TestWise IDE. I never needed to do this kind of syntax change for my Selenium Ruby tests, since 2011.
Furthermore, there are ECMAScript, Coffee Script, TypeScript, …, etc. Do you feel your head spinning?
Concise
Less code, without compromises of readability and performance, is always welcomed. Here, I will compare some examples in common (not tricky) ways.
1. Convert to integer
Ruby:
“10”.to_i
JavaScript:
parseInt("10")
2. Looping an array
Ruby:
arr = [1,2,3,4]
arr.each {|n| puts n }10.times do
# do stuff
end
JavaScript:
var arr = [1,2,3,4]
arr.forEach(function(n) {
console.log(n);
});for (let i = 0; i < 10; i++) {
// do stuff
}
3. No function call parentheses
Ruby:
login "agileway", "testwise"
login("agileway", "testwise") # works too
JavaScript:
login("agileway", "testwise")
4. Implicit returns
Ruby:
def sum(a, b)
a + b
end
JavaScript:
function sum(a, b) {
return a + b;
}
5. Utilities that often used in automated test script
Return a random value in an array.
Ruby:
["Male", "Female"].sample
JavaScript:
let arr = ["Male", "Female"]
arr[Math.floor(Math.random() * arr.length)]
Flexible
Ruby is a true dynamic Object-Oriented scripting language, JavaScript is not. For example, everything in Ruby is an Object. Duck typing (“If it walks like a duck and it quacks like a duck, then it must be a duck”) makes it really flexible.
Here I will just show one common use in automated test scripts: generating a dynamic date.
Ruby:
3.days.ago
JavaScript:
let today = Date.now();
new Date(today - 86400000 * 3).toISOString()
Easy to Set up
When introducing test automation to non-tech team members, such as manual testers, business analysts, customers, and project managers, it is important to get it up and running very quickly (in minutes) on their machines. Otherwise, they will lose attention. To be clear, this means a business analyst, under my instruction, can perform the following
Install Test Tool (and language runtime and dependent libraries)
Install ChromeDriver
Get the test script
Find a test and run it
All the above, under 5 minutes or less (performed by them, not me). For over 10+ years, it is possible with Ruby test scripts using the TestWise tool.
Check out my daughter’s article: Set up, Develop Automated UI tests and Run them in a CT server on your First day at work.
A Testimony
My daughter started writing raw Selenium tests in Ruby at the age of 12. A subjective case, I agree, so I will show another one.
Update: I am converting a successful coaching 2 complete non-IT laymen test automation into a new “Web Test Automation with Selenium WebDriver Workshop” series on Substack.
Below is a LinkedIn message sent by a manual tester whom I started mentoring at 3 PM. She created two real work tests based on my example, under my coaching. At night (the same day), she asked me which language she was using.
Also, my former mentee (very smart and learned well under my mentoring) failed test automation (and then gave up, pity!) after using JavaScript-based Protractor in his new company.
Story: A Former Mentee Failed Test Automation with Protractor.
Related reading:
My eBooks:
- Practical Web Test Automation with Selenium WebDriver
- Practical Continuous Testing: make Agile/DevOps real
- Practical Performance and Load TestingAn ebook authored by me and my daughter:
- Learn Ruby Programming by ExamplesAdvice on Self-Learning Test Automation with Selenium WebDriver
Benefits of Test Automation and Continuous Testing series: Executives, Managers, Business Analysts, Developers, Testers, and Customers.
Protractor, another Automation Framework I rightly avoided, is being Deprecated