Polyglot Automation
@yashaka
Preface…
@yashaka
What Language?
Automation =
Automating manual routine work
Automation => QA ???
Effective Automation => QA
Polyglot Automation=
Using Any Language to make it Effective
Effective Automation *=
with Backend Language
Web UI
Why?
Developers can help with implementation
Easier for devs to use and support tests
Smaller Tech Stack. No Zoo at project :)
And…
Allows to use fast & efficient test data pre-setup!
Demo
Full coverage for TodoMVC:
Without “backend” preconditions: 24s
With “backend” preconditions: 16s
=> optimised for 30%
A remark
TodoMVC is very simple app with small lightweight “preconditions” in tests
Real app will usually have much more “hardweight” preconditions
So the optimisation may be much bigger
But it’s hard, no?
Hard to:
learn new language…
use statically typed Java/C# over dynamic Python/Ruby…
?
No:p
No:pif you use “easy tools” ;)
Examples
Tests
public class TodoMVCTest { @Test public void testFilterTasks(){
Tasks.visit();
Tasks.add("a", "b", "c"); Tasks.shouldBe("a", "b", "c");
Tasks.toggle("b");
Tasks.filterActive (); Tasks.shouldBe("a", "c");
Tasks.filterCompleted (); Tasks.shouldBe("b"); }}
[TestFixture ()]public class Test : BaseTest { [Test ()] public void FilterTasks () { Tasks.Visit ();
Tasks.Add ("a", "b", "c"); Tasks.ShouldBe ("a", "b", "c");
Tasks.Toggle ("b");
Tasks.FilterActive (); Tasks.ShouldBe ("a", "c");
Tasks.FilterCompleted (); Tasks.ShouldBe ("b"); }}
class TestTodoMVC(BaseTest): def test_filter_tasks(self):
tasks.visit()
tasks.add("a", "b", "c") tasks.should_be("a", "b", "c")
tasks.toggle("b")
tasks.filter_active () tasks.should_be("a", "c")
tasks.filter_completed () tasks.should_be("b")
describe "todomvc" do it "filters tasks" do
Tasks.open
Tasks.add "a", "b", "c" Tasks.should_be "a", "b", "c"
Tasks.toggle "b"
Tasks.filter_active Tasks.should_be "a", "c"
Tasks.filter_completed Tasks.should_be "b" endend
Helperssimpler
public class Tasks { ... public static void visit() {
open("https://todomvc4tasj.herokuapp.com/"); } public static void filterActive(){
$(By.linkText("Active")).click(); } public static void filterCompleted(){
$(By.linkText("Completed")).click(); } public static void add(String... taskTexts) {
for(String text: taskTexts){
$("#new-todo").setValue(text).pressEnter(); } } ...}
public static class Tasks{ ... public static void Visit() {
Open ("https://todomvc4tasj.herokuapp.com/"); } public static void FilterActive () {
S (By.LinkText ("Active")).Click (); } public static void FilterCompleted () {
S (By.LinkText ("Completed")).Click (); } public static void Add(params string[] taskTexts) {
foreach (var text in taskTexts) {
S ("#new-todo").SetValue (text).PressEnter (); } } ...}
…
def visit():
tools.visit("https://todomvc4tasj.herokuapp.com/")def filter_active():
s(by_link_text(”Active”)).click()def filter_completed():
s(by_link_text(”Completed”)).click()def add(*task_texts):
for text in task_texts:
s("#new-todo").set_value(text).press_enter()
module Tasks extend Capybara::DSL def self.open
visit 'https://todomvc4tasj.herokuapp.com' end def self.filter_active
find("a", text:"Active").click end def self.filter_completed
find("a", text:"Completed").click end def self.add *task_texts
task_texts.each do |text|
find("#new-todo").send_keys(text, :enter) end end ...end
Helpersa bit harder
public class Tasks {
public static ElementsCollection tasks = $$("#todo-list>li"); ... public static void toggle(String taskText) { tasks.findBy(exactText(taskText)).find(".toggle").click(); } public static void shouldBe(String... taskTexts) { tasks.filterBy(visible).shouldHave(exactTexts(taskTexts)); }}
public static class Tasks{ public static SCollection List = SS ("#todo-list>li"); ... public static void Toggle (string taskText) { List.FindBy (Have.ExactText(taskText)).Find (".toggle").Click(); } public static void ShouldBe(params string[] names) { List.FilterBy (Be.Visible).Should (Have.Texts (names)); }}
tasks = ss("#todo-list>li")...
def toggle(task_text): tasks.findBy(exact_text(task_text)).find(".toggle").click() def should_be(*task_texts): tasks.filterBy(visible).should_have(exact_texts(*task_texts))
module Tasks extend Capybara::DSL def self.tasks all "#todo-list>li" end ... def self.toggle task_text
(tasks.findBy {|task| task.text == task_text}).find(".toggle").click end def self.should_be *task_texts tasks.texts.should == task_texts endend
Easy?
Easy Tools?
Java: Selenide
Python: Selene is coming…
C#: NSelene is coming…
Ruby: Capybara
? JavaScript: Protractor
? PHP: Codeception
No ready Easy Tool for your language?
Lack of knowledge?
=> ask project’s developers to write it for you
Brave?
=> implement it by your own
How to become polyglot automation engineer?
Just learn ONE Language perfectly.
Then
Don’t be afraid to switch to other when it fits better
Learn next language
learnxinyminutes.com +
koans +
practice solving common tasks +
google.com
AfterwordsThere are good practices in context,
but there are no best practices. (c) Cem Kaner, James Bach
Q&A
github.com/yashaka
youtube.com/c/ItlabsNetUa
gitter.im/yashaka/better-selenium
[email protected] @yashaka