Ryan Pollard ryan.pollard.world Dylan Just dylan@techtangents.com - - PowerPoint PPT Presentation

ryan pollard ryan pollard world dylan just dylan
SMART_READER_LITE
LIVE PREVIEW

Ryan Pollard ryan.pollard.world Dylan Just dylan@techtangents.com - - PowerPoint PPT Presentation

Ryan Pollard ryan.pollard.world Dylan Just dylan@techtangents.com Flax A good source of Selenium The Problem Legacy product needed testing. Scala codebase. Product UI was very complex. Needed simple tests. Goals Nice,


slide-1
SLIDE 1

Ryan Pollard ryan.pollard.world Dylan Just dylan@techtangents.com

slide-2
SLIDE 2

Flax

A good source of Selenium

slide-3
SLIDE 3

The Problem

  • Legacy product needed testing.
  • Scala codebase.
  • Product UI was very complex.
  • Needed simple tests.
slide-4
SLIDE 4
slide-5
SLIDE 5
slide-6
SLIDE 6

Goals

  • Nice, high-level language.
  • Usable by testers.
  • Clean syntax - readable by non-testers.
  • Feedback on failures.
  • To FP or not to FP?
slide-7
SLIDE 7
slide-8
SLIDE 8

!|script |com.ephox.webradar.fitnesse.fixtures.WebRadarFixture | |open url |http://myserver:10039/wps/portal | |click link having text |Log In | |enter text |username |in textbox having id|userID | |enter text |mypassword |in textbox having id|password | |click element having id |login.button.login | |verify text |wpsadmin |appears on page |

slide-9
SLIDE 9
slide-10
SLIDE 10

Anyone promoting tools where you can ‘program without programming’ is just selling you a terrible programming language.

slide-11
SLIDE 11

Let's use a programming language!!!

slide-12
SLIDE 12

Let's use a programming language!!!

slide-13
SLIDE 13

Selenium Java Client

  • No effect-tracking.
  • Exceptions and nulls.
  • Mutable data.
  • Messy syntax.
  • Java.
slide-14
SLIDE 14

Selenium Java Client

slide-15
SLIDE 15

Selenium Java Client

Not found? Not reachable? Could return null/exception? Passing Driver around!

slide-16
SLIDE 16
slide-17
SLIDE 17

def changeLocale(locale: Locale) (implicit config: TestConfig, driver: WebDriver): Unit = { goToUserSettings typeInCurrentPassword setLocale(locale) clickOk waitForPageToGoAway }

Exceptions Implicits

slide-18
SLIDE 18

Different Shapes

slide-19
SLIDE 19

FP

slide-20
SLIDE 20

FP

slide-21
SLIDE 21

FP

slide-22
SLIDE 22

Flax Attacks

slide-23
SLIDE 23

Inside the Flax Shack

  • Import Specs2.
  • Import Flax.
  • Make tests go now.
slide-24
SLIDE 24

Flax Strikes Back

github.com/idempotency/investo

slide-25
SLIDE 25
slide-26
SLIDE 26
slide-27
SLIDE 27

Thanks, Functional Programming.

Thunktional Programming.

slide-28
SLIDE 28

Code that describes things FP Code that does things

slide-29
SLIDE 29

Describe: Testing with Selenium

slide-30
SLIDE 30

We need a Selenium WebDriver object

slide-31
SLIDE 31

Side effects: browser interaction

slide-32
SLIDE 32

Logs: what actions were performed?

slide-33
SLIDE 33

Tests may pass or fail

slide-34
SLIDE 34

A Read values from the browser

slide-35
SLIDE 35

A

slide-36
SLIDE 36

A

slide-37
SLIDE 37

"Action"

slide-38
SLIDE 38

An Action takes a WebDriver as input.

slide-39
SLIDE 39

Reader

case class Reader[T, A] = Reader(f: T => A) Reader[Driver, A]

Reader[T, A] T => A

slide-40
SLIDE 40

An Action may perform effects on the browser

slide-41
SLIDE 41

IO*

IO[A]

!

A

def apply[A](a: => A): IO[A]

unsafePerformIO: A

slide-42
SLIDE 42

An Action may produce log messages.

slide-43
SLIDE 43

Writer

case class Writer[W, A] = Writer (run: (W, A)) Writer[Log[String], A]

Writer[W, A] W A write / accumulate return

slide-44
SLIDE 44

An Action may pass or fail.

slide-45
SLIDE 45

Either

Err \/ A Err

  • Test Assertion Failed
  • Could Not Find Element
  • Wrong Element Type
  • Kersploded
  • Other
slide-46
SLIDE 46

A

slide-47
SLIDE 47

Reader IO Writer Either A

slide-48
SLIDE 48

IO Reader Writer Either A

slide-49
SLIDE 49

EitherT WriterT ReaderT IO A

slide-50
SLIDE 50

EitherT WriterT ReaderT IO

Monad Transformers form monads for stacks of monadic things

slide-51
SLIDE 51

case class Action(run: EitherT[ WriterT[ ReaderT[IO, Driver, ?], Log[String], ?], Err, A ])

slide-52
SLIDE 52
slide-53
SLIDE 53

What do we want to do with Actions?

  • Create them.
  • "Chain" them together
  • Execute them as tests.
slide-54
SLIDE 54

Create

  • logOnly(String)
  • fromSideEffect((d: Driver) => ...)
  • assert(x must_=== y)
  • find(By)
  • click(Elem)
slide-55
SLIDE 55

def fromSideEffectWithLog[A]( msg: String, run: Driver => A): Action[A] = … def get(url: String): Action[Unit] = fromSideEffectWithLog( "Opening URL: " + url, driver => driver.d.get(url) )

Create

slide-56
SLIDE 56

Chaining actions?

slide-57
SLIDE 57
slide-58
SLIDE 58
slide-59
SLIDE 59

Monading

def setTextBy(by: By, text: String): Action[Unit] = for { e <- find(by) _ <- clear(e) _ <- typeIn(e, text) _ <- click("submit") _ < assert } yield ()

slide-60
SLIDE 60

Stack traces vs Logs

slide-61
SLIDE 61

driver.get("https://twitter.com/"); driver.findElement(By.xpath("//input[@name='session[username_or_email]']")) .sendKeys(username); driver.findElement(By.xpath("//input[@name='session[password]']")) .sendKeys(password); driver.findElement(By.xpath("//input[@value='Log on']")).click(); doSomethingElse();

Executing

*** Element info: {Using=xpath, value=//input[@value='Log on']} ... at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:545) at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:319) at org.openqa.selenium.remote.RemoteWebDriver.findElementByXPath(RemoteWebDriver.java:421) at org.openqa.selenium.By$ByXPath.findElement(By.java:361) at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:311) at com.ephox.flax.it.MyTest.main(MyTest.java:20)

slide-62
SLIDE 62

"My test" >> { for { _ <- get("https://twitter.com/") _ <- typeInBy(xpath("//input[@name='session[username_or_email]']"), username) _ <- typeInBy(xpath("//input[@name='session[password]']"), password) _ <- clickBy(xpath("//input[@value='Log in']")) } yield () } ... a.runAsResult(driver)

Composing Executing

slide-63
SLIDE 63

Log

final case class Log[A](list: DList[Node[A]]) final case class Node[A](label: A, children: Log[A])

Nodes Log Action

slide-64
SLIDE 64

logOnly("pen")

pen

slide-65
SLIDE 65

logOnly("pen")

pen apple

logOnly("apple")

slide-66
SLIDE 66

*>

slide-67
SLIDE 67

apple

logOnly("apple") *> logOnly("pen")

pen

slide-68
SLIDE 68

nested("Open twitter homepage", get("https://twitter.com/"))

Open twitter homepage Opening URL: https://twitter.com/

slide-69
SLIDE 69

nested("Enter Credentials", for { _ <- typeInBy(xpath("//...."), username) _ <- typeInBy(xpath("//...."), password) }

Enter Credentials Type in by xpath... Type in by xpath...

slide-70
SLIDE 70

def signInToTwitter: Action[Unit] = nested("Sign in to Twitter", for { _ <- openTwitterHomepage _ <- enterCredentials _ <- clickLoginButton } yield ()) def openTwitterHomepage: Action[Unit] = nested("Open twitter homepage", get("https://twitter.com/")) def enterCredentials: Action[Unit] = nested("Enter Credentials", for { _ <- typeInBy(xpath("//input[@name='session[username_or_email]']"), username) _ <- typeInBy(xpath("//input[@name='session[password]']"), password) } yield ()) def clickLoginButton: Action[Unit] = nested("Click login button", clickBy(xpath("//input[@value='Lorg in']")))

slide-71
SLIDE 71

Steps performed:

  • Signing in to Twitter
  • Open twitter homepage
  • Opening URL: https://twitter.com/
  • Enter Credentials
  • Finding element: By.xpath: //input[@name='session[username_or_email]']
  • Typing "flax_demo" in element: By.xpath: //input[@name='session[username_or_email]']
  • Finding element: By.xpath: //input[@name='session[password]']
  • Typing "flaxtwitter" in element: By.xpath: //input[@name='session[password]']
  • Click login button
  • Finding element: By.xpath: //input[@value='Lorg in']

!!! Could not find element: By.xpath: //input[@value='Lorg in']

slide-72
SLIDE 72

Conclusion

  • FP gave practical benefits.
  • Scala "for" comprehensions.
  • Monad transformers - hard in Scala, but helpful
  • Log tree.
  • FP data types modelled key aspects of behaviour.
slide-73
SLIDE 73

Ryan Pollard ryan.pollard.world Dylan Just dylan@techtangents.com

slide-74
SLIDE 74

Workshop

  • 1. Set up Intellij and sbt shell command.
  • 2. "Getting started" instructions.
  • 3. Test some things. Things to try:

a.

Open a site

b.

Click a button

c.

Type text

d.

Nest actions

Example scenarios follow...

slide-75
SLIDE 75

Test ephox.com

https://www.ephox.com/ Click "compare rich text editors" Click the "learn more" link - this takes us to tinymce.com Execute some js to get the content from the tinymce instance Get the "quick start" html text.

slide-76
SLIDE 76

Test docs

https://www.tinymce.com/docs/ Search for "hybrid". Click on the links.

slide-77
SLIDE 77

assertions

Use FlaxAssertions.assert to assert some simple properties on the previous examples. Observe test passes and failures.

slide-78
SLIDE 78

Implementing Actions

Check out the Flax code from github Implement the api from the selenium "Select" in the "Selekt" wrapper class. See "Elem" for an example. Send a PR :)