Libraries

ScalaTest + Play

ScalaTest + Selenium

ScalaTest + ScalaCheck

ScalaTest + JUnit 4

ScalaTest + JUnit 5

ScalaTest + TestNG

ScalaTest + EasyMock

ScalaTest + JMock

ScalaTest + Mockito

Using Selenium

ScalaTest + Selenium includes a domain specific language (DSL) for writing browser-based tests using Selenium.

To use ScalaTest's Selenium DSL, you'll first need to add ScalaTest + Selenium to your project dependencies. For example, in your sbt build you might add:

libraryDependencies += "org.scalatestplus" %% "selenium-4-17" % "3.2.18.0" % "test"

Note, that the selected selenium version for the sclatestplus library should reflect the version you have used for selenium-java and the version itself is derived from the scalatest core library being used.

For following examples illustration purpose, we'll add in another two scalatest dependencies for FlatSpec style trait and Should Matchers DSL:
libraryDependencies += "org.scalatest" %% "scalatest-flatspec" % "3.2.18" % "test"
libraryDependencies += "org.scalatest" %% "scalatest-shouldmatchers" % "3.2.18" % "test"

Then mix trait WebBrowser into your test class. This trait provides the DSL in its entirety except for one missing piece: an implicit org.openqa.selenium.WebDriver. One way to provide the missing implicit driver is to declare one as a member of your test class, like this:

import org.scalatest._
import org.scalatest.matchers._
import org.scalatestplus.selenium._
import org.openqa.selenium.WebDriver
import org.openqa.selenium.htmlunit.HtmlUnitDriver

class BlogSpec extends flatspec.AnyFlatSpec with should.Matchers with WebBrowser {

  implicit val webDriver: WebDriver = new HtmlUnitDriver

  val host = "http://localhost:9000/"

  "The blog app home page" should "have the correct title" in {
    go to (host + "index.html")
    pageTitle should be ("Awesome Blog")
  }
}

For convenience, however, ScalaTest provides a WebBrowser subtrait containing an implicit WebDriver for each driver provided by Selenium. Thus a simpler way to use the HtmlUnit driver, for example, is to extend ScalaTest's HtmlUnit trait, like this:

import org.scalatest._
import org.scalatest.matchers._
import org.scalatestplus.selenium._  

class BlogSpec extends flatspec.AnyFlatSpec with should.Matchers with HtmlUnit {

  val host = "http://localhost:9000/"

  "The blog app home page" should "have the correct title" in {
    go to (host + "index.html")
    pageTitle should be ("Awesome Blog")
  }
}

The web driver traits provided by ScalaTest are:

DriverWebBrowser subtrait
Google Chrome Chrome
Mozilla Firefox Firefox
HtmlUnit HtmlUnit
Microsoft Internet Explorer InternetExplorer
Apple Safari Safari

Navigation

You can ask the browser to retrieve a page (go to a URL) like this:

go to "http://www.artima.com"

Note: If you are using the page object pattern, you can also go to a page using the Page instance, as illustrated in the section on page objects below.

Once you have retrieved a page, you can fill in and submit forms, query for the values of page elements, and make assertions. In the following example, selenium will go to http://www.google.com, fill in the text box with Cheese!, press the submit button, and wait for result returned from an AJAX call:

go to "http://www.google.com"
click on "q"
textField("q").value = "Cheese!"
submit()
// Google's search is rendered dynamically with JavaScript.
eventually { title should be ("Cheese! - Google Search") }

In the above example, the "q" used in “click on "q"” and “textField("q")” can be either the id or name of an element. ScalaTest's Selenium DSL will try to lookup by id first. If it cannot find any element with an id equal to "q", it will then try lookup by name "q".

Alternatively, you can be more specific:

click on id("q")   // to lookup by id "q" 
click on name("q") // to lookup by name "q" 

In addition to id and name, you can use the following approaches to lookup elements, just as you can do with Selenium's org.openqa.selenium.By class:

  • xpath
  • className
  • cssSelector
  • linkText
  • partialLinkText
  • tagName

For example, you can select by link text with:

click on linkText("click here!")

If an element is not found via any form of lookup, evaluation will complete abruptly with a TestFailedException.

Getting and setting input element values

ScalaTest's Selenium DSL provides a clear, simple syntax for accessing and updating the values of input elements such as text fields, radio buttons, checkboxes, and selection lists. If a requested element is not found, or if it is found but is not of the requested type, an exception will immediately result causing the test to fail.

Text fields and text areas

You can change a text field's value by assigning it via the = operator, like this:

textField("q").value = "Cheese!"

And you can access a text field's value by simply invoking value on it:

textField("q").value should be ("Cheese!")

If the text field is empty, value will return an empty string ("").

You can use the same syntax with text areas by replacing textField with textArea, as in:

textArea("body").value = "I saw something cool today!"
textArea("body").value should be ("I saw something cool today!")

Radio buttons

Radio buttons work together in groups. For example, you could have a group of radio buttons, like this:

<input type="radio" name="group1" value="Option 1"> Option 1</input>
<input type="radio" name="group1" value="Option 2"> Option 2</input>
<input type="radio" name="group1" value="Option 3"> Option 3</input>

You can select an option in either of two ways:

radioButtonGroup("group1").value = "Option 2"
radioButtonGroup("group1").selection = Some("Option 2")

Likewise, you can read the currently selected value of a group of radio buttons in two ways:

radioButtonGroup("group1").value should be ("Option 2")
radioButtonGroup("group1").selection should be (Some("Option 2"))

If the radio button has no selection at all, selection will return None whereas value will throw a TestFailedException. By using value, you are indicating you expect a selection, and if there isn't a selection that should result in a failed test.

Checkboxes

A checkbox in one of two states: selected or cleared. Here's how you select a checkbox:

checkbox("cbx1").select()

And here's how you'd clear one:

checkbox("cbx1").clear()

You can access the current state of a checkbox with isSelected:

checkbox("cbx1").isSelected should be (true)

Single-selection dropdown lists

Given the following single-selection dropdown list:

<select id="select1">
 <option value="option1">Option 1</option>
 <option value="option2">Option 2</option>
 <option value="option3">Option 3</option>
</select>

You could select Option 2 in either of two ways:

singleSel("select1").value = "option2"
singleSel("select1").selection = Some("option2")

To clear the selection, either invoke clear or set selection to None:

singleSel.clear()
singleSel("select1").selection = None

You can read the currently selected value of a single-selection list in the same manner as radio buttons:

singleSel("select1").value should be ("option2")
singleSel("select1").selection should be (Some("option2"))

If the single-selection list has no selection at all, selection will return None whereas value will throw a TestFailedException. By using value, you are indicating you expect a selection, and if there isn't a selection that should result in a failed test.

Multiple-selection lists

Given the following multiple-selection list:

<select name="select2" multiple="multiple">
 <option value="option4">Option 4</option>
 <option value="option5">Option 5</option>
 <option value="option6">Option 6</option>
</select>

You could select Option 5 and Option 6 like this:

multiSel("select2").values = Seq("option5", "option6")

The previous command would essentially clear all selections first, then select Option 5 and Option 6. If instead you want to not clear any existing selection, just additionally select Option 5 and Option 6, you can use the += operator, like this.

multiSel("select2").values += "option5"
multiSel("select2").values += "option6"

To clear a specific option, pass its name to clear:

multiSel("select2").clear("option5")

To clear all selections, call clearAll:

multiSel("select2").clearAll()

You can access the current selections with values, which returns an IndexedSeq[String]:

multiSel("select2").values should have size 2
multiSel("select2").values(0) should be ("option5")
multiSel("select2").values(1) should be ("option6")

Clicking and submitting

You can click on any element with “click on” as shown previously:

click on "aButton"
click on name("aTextField")

If the requested element is not found, click on will throw an exception, failing the test.

Clicking on a input element will give it the focus. If current focus is in on an input element within a form, you can submit the form by calling submit:

submit()

Switching

You can switch to a popup alert using the following code:

switch to alert

to switch to a frame, you could:

switch to frame(0) // switch by index
switch to frame("name") // switch by name

If you have reference to a window handle (can be obtained from calling windowHandle/windowHandles), you can switch to a particular window by:

switch to window(windowHandle)

Similar to what you got in Selenium, you can also switch to active element and default content:

switch to activeElement
switch to defaultContent

Navigation history

In real web browser, you can press the 'Back' button to go back to previous page. To emulate that action in your test, you can call goBack:

goBack()

To emulate the 'Forward' button, you can call:

goForward()

And to refresh or reload the current page, you can call:

reloadPage()

Cookies!

To create a new cookie, you'll say:

add cookie ("cookie_name", "cookie_value")

to read a cookie value, you do:

// If value is undefined, throws TFE right then and there. Never returns null.
cookie("cookie_name").value should be ("cookie_value")

In addition to the common use of name-value cookie, you can pass these extra fields when creating the cookie, available ways are:

cookie(name: String, value: String)
cookie(name: String, value: String, path: String)
cookie(name: String, value: String, path: String, expiry: Date)
cookie(name: String, value: String, domain: String, path: String, expiry: Date)
cookie(name: String, value: String, domain: String, path: String, expiry: Date, secure: Boolean)

and to read those extra fields:

cookie("cookie_name").value   // Read cookie's value
cookie("cookie_name").path    // Read cookie's path
cookie("cookie_name").expiry  // Read cookie's expiry
cookie("cookie_name").domain  // Read cookie's domain
cookie("cookie_name").isSecure  // Read cookie's isSecure flag

In order to delete a cookie, you could use the following code:

delete cookie "cookie_name"

or to delete all cookies in the same domain:-

delete all cookies

Implicit wait

To set the implicit wait, you can call implicitlyWait method:

implicitlyWait(Span(10, Seconds))

Page source and current URL

It is possible to get the html source of currently loaded page, using:

pageSource

and if needed, get the current URL of currently loaded page:

currentUrl

Screen capture

You can capture screen using the following code:

val file = capture

By default, the captured image file will be saved in temporary folder (returned by java.io.tmpdir property), with random file name ends with .png extension. You can specify a fixed file name:

capture to "MyScreenShot.png"

or

capture to "MyScreenShot"

Both will result in a same file name MyScreenShot.png.

You can also change the target folder screenshot file is written to, by saying:

setCaptureDir("/home/your_name/screenshots")

If you want to capture a screenshot when something goes wrong (e.g. test failed), you can use withScreenshot:

withScreenshot {
  assert("Gold" == "Silver", "Expected gold, but got silver")
}

In case the test code fails, you'll see the screenshot location appended to the error message, for example:

Expected gold but got silver; screenshot capture in /tmp/AbCdEfGhIj.png

Using the page object pattern

If you use the page object pattern, mixing trait Page into your page classes will allow you to use the go to syntax with your page objects. Here's an example:

class HomePage extends Page {
  val url = "localhost:9000/index.html"
}

val homePage = new HomePage go to homePage

Executing JavaScript

To execute arbitrary JavaScript, for example, to test some JavaScript functions on your page, pass it to executeScript:

go to (host + "index.html")
val result1 = executeScript("return document.title;")
result1 should be ("Test Title")
val result2 = executeScript("return 'Hello ' + arguments[0]", "ScalaTest")
result2 should be ("Hello ScalaTest")

To execute an asynchronous bit of JavaScript, pass it to executeAsyncScript. You can set the script timeout with setScriptTimeout:

val script = """
  var callback = arguments[arguments.length - 1];
  window.setTimeout(function() {callback('Hello ScalaTest')}, 500);
"""
setScriptTimeout(1 second)
val result = executeAsyncScript(script)
result should be ("Hello ScalaTest")

Querying for elements

You can query for arbitrary elements via find and findAll. The find method returns the first matching element, wrapped in a Some, or None if no element is found. The findAll method returns an IndexedSeq of all matching elements. If no elements match the query, findAll returns an empty IndexedSeq. These methods allow you to perform rich queries using for expressions. Here are some examples:

val ele: Option[Element] = find("q")

val eles: IndexedSeq[Element] = findAll(className("small")) for (e <- eles; if e.tagName != "input") e should be ('displayed) val textFields = eles filter { tf.isInstanceOf[TextField] }

Cleaning up

To close the current browser window, and exit the driver if the current window was the only one remaining, use close:

close()

To close all windows, and exit the driver, use quit:

quit()

ScalaTest is brought to you by Bill Venners and Artima.
ScalaTest is free, open-source software released under the Apache 2.0 license.

If your company loves ScalaTest, please consider sponsoring the project.

Copyright © 2009-2024 Artima, Inc. All Rights Reserved.

artima