ScalaTest has very good documentation but they are short and do not give an example of
acceptance test.
How can I write acceptance test using ScalaTest for a web application?
Using Selenium 2 gets you some mileage. I'm using Selenium 2 WebDriver in combination with a variation of the Selenium DSL found here.
Originally, I changed the DSL in order to make it a little easier to run in from the REPL (see down below). However, one of the bigger challenges of building tests like these is that they quickly get invalidated, and then become a nightmare to maintain.
Later on, I started creating a wrapper class for every page in the application, with convenience operations mapping the event to be sent to that page to the underlying WebDriver invocations. That way, whenever the underling page is changing, I just need to change my page wrapper, rather than changing the entire script. With that, my test scripts are now expressed in terms of invocations on the individual page wrappers, where every invocation returns a page wrapper reflecting the new state of the UI. Seems to work out quite well.
I tend to build my tests with the FirefoxDriver and then before rolling the test to our QA environment check if the HtmlUnit driver is giving comparable results. If that holds, then I run the test using the HtmlUnit driver.
This was my original modification to the Selenium DSL:
/**
* Copied from [[http://comments.gmane.org/gmane.comp.web.lift/44563]], adjusting it to no longer be a trait that you need to mix in,
* but an object that you can import, to ease scripting.
*
* With this object's method imported, you can do things like:
*
* {{"#whatever"}}: Select the element with ID "whatever"
* {{".whatever"}}: Select the element with class "whatever"
* {{"%//td/em"}}: Select the "em" element inside a "td" tag
* {{":em"}}: Select the "em" element
* {{"=whatever"}}: Select the element with the given link text
*/
object SeleniumDsl {
private def finder(c: Char): String => By = s => c match {
case '#' => By id s
case '.' => By className s
case '$' => By cssSelector s
case '%' => By xpath s
case ':' => By name s
case '=' => By linkText s
case '~' => By partialLinkText s
case _ => By tagName c + s
}
implicit def str2by(s: String): By = finder(s.charAt(0))(s.substring(1))
implicit def by2El[T](t: T)(implicit conversion: (T) => By, driver: WebDriver): WebElement = driver / (conversion(t))
implicit def el2Sel[T <% WebElement](el: T): Select = new Select(el)
class Searchable(sc: SearchContext) {
def /[T <% By](b: T): WebElement = sc.findElement(b)
def /?[T <% By](b: T): Box[WebElement] = tryo(sc.findElement(b))
def /+[T <% By](b: T): Seq[WebElement] = sc.findElements(b)
}
implicit def scDsl[T <% SearchContext](sc: T): Searchable = new Searchable(sc)
}
ScalaTest now offers a Selenium DSL:
http://www.scalatest.org/user_guide/using_selenium
Related
I've got an API that looks like this:
object Comics {
...
def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
...
and a routing that looks like this:
object Routes {
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap {
case Some(author) => Ok(author)
case None => NotFound()
}
So when there is a None, it gets converted to a 404. Since there are several routes, the .flatMap { ... } gets duplicated.
Question: How do I move this into a separate .orNotFound helper function specific to my project?
My attempt:
To make things simple for me (and avoid parameterisation over F initially), I've tried to define this inside comicsRoutes:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A]): ???[A] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound)
But what's ??? here? It doesn't seem to be Response or Status. Also, the .flatMap { ... } was made under import dsl._, but I'd like to move this further out. What would a good place be? Does it go into the routes file, or do I put it in a separate ExtendedSomething extension file? (I expect that ??? and Something might be related, but I'm a little confused as to what the missing types are.)
(Equally importantly, how do I find out what ??? is here? I was hoping ??? at the type level might give me a "typed hole", and VSCode's hover function provides very sporadic documentation value.)
The type returned by Http4sDsl[F] for your actions is F[Response[F]].
It has to be wrapped with F because your are using .flatMap on F. Response is parametrized with F because it will produce the result returned to caller using F.
To find that out you can use IntelliJ and then generate the annotation by IDE (Alt+Enter and then "Add type annotation to value definition"). You can also:
preview implicits to check that Ok object imported from Statuses trait is provided extension methods with http4sOkSyntax implicit conversion (Ctrl+Alt+Shift+Plus sign, you can press it a few times to expand implicits more, and Ctrl+Alt+Shift+Minut to hide them again)
find http4sOkSyntax by pressing Shift twice to open find window, and then pressing it twice again to include non-project symbols,
from there navigate with Ctrl+B through OkOps to EntityResponseGenerator class which is providing you the
functionality you used (in apply) returning F[Resposne[F]].
So if you want to move things around/extract them, pay attention to what implicits are required to instantiate the DSL and extension methods.
(Shortcuts differ between Mac OS - which sometime use Cmd instead of Ctrl - and non-Mac OS systems so just check them in documentation if you have an issue).
Thanks to Mateusz, I learned that ??? should be F[Response[F]].
To make this helper function work fully, two more type-related problems occurred:
Since value: A is polymorphic, Http4s expects an implicit EntityEncoder[F, A] in order to serialize an arbitrary value. (This was not a problem with the original { case ... } match, since the type was concrete and not polymorphic.
Adding this implicit annotation was, for some reason, not enough. Doing .flatMap(orNotFound) fails type inference. Doing .flatMap(orNotFound[Authors.Slug]) fixes this.
(Thanks to keynmol for pointing out the other two.)
Having all three changes, this results in:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A])(implicit ee: EntityEncoder[F, A]): F[Response[F]] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound[Authors.Author])
...
For accessing objects, a Slick DAO which contains functions returning actions and objects of a stored type were created. Example:
def findByKeysAction(a: String, b: String, c: String = {
Users.filter(x => x.a === a && x.b === b && x.c === c).result
}
def findByKeys(a: String, b: String, c: String): Future[Option[foo]] = {
db.run(findByKeysAction(consumerId, contextId, userId)).map(_.headOption)
}
Notice how the non-action-based function wraps the other in db.run().
What is a solid approach to test both functions and minimizing redundancy of code?
I naive method could of course be to test them both with their individual test setups (above is a simple example; there could be a lot of test setup needed to satisfy DB restrictions).
Notice how the non-action-based function wraps the other in db.run().
Not really. Your findByKeys method does not call findByUserIdAction, so I'm adjusting for that minor detail in this answer.
def findByUserIdAction(userId: String) = {
Users.filter(_.userId === userId).result
}
The above code returns a DBIOAction. As the documentation states:
Just like a query, an I/O action is only a description of an operation. Creating or composing actions does not execute anything on a database.
As far as a user of Slick is concerned, there is no meaningful test for a DBIOAction, because by itself it does nothing; it's only a recipe for what one wants to do. To execute the above DBIOAction, you have to materialize it, which is what the following does:
def findByUserId(userId: String): Future[Option[User]] = {
db.run(findByUserIdAction(userId)).map(_.headOption)
}
The materialized result is what you want to test. One way to do so is to use ScalaTest's ScalaFutures trait. For example, in a spec that mixes in that trait, you could have something like:
"Users" should "return a single user by id" in {
findByUserId("id3").futureValue shouldBe Option(User("id3", ...))
}
Take a look at this Slick 3.2.0 test project for more examples: specifically, TestSpec and QueryCoffeesTest.
In summary, don't bother trying to test a DBIOAction in isolation; just test its materialized result.
Using Scala, Play Framework, Slick 3, Specs2.
I have a repository layer and a service layer. The repositories are quite dumb, and I use specs2 to make sure the service layer does its job.
My repositories used to return futures, like this:
def findById(id: Long): Future[Option[Foo]] =
db.run(fooQuery(id))
Then it would be used in the service:
def foonicate(id: Long): Future[Foo] = {
fooRepository.findById(id).flatMap { optFoo =>
val foo: Foo = optFoo match {
case Some(foo) => [business logic returning Foo]
case None => [business logic returning Foo]
}
fooRepository.save(foo)
}
}
Services were easy to spec. In the service spec, the FooRepository would be mocked like this:
fooRepository.findById(3).returns(Future(Foo(3)))
I recently found the need for database transactions. Several queries should be combined into a single transaction. The prevailing opinion seems to be that it's perfectly ok to handle transaction logic in the service layer.
With that in mind, I've changed my repositories to return slick.dbio.DBIO and added a helper method to run the query transactionally:
def findById(id: Long): DBIO[Option[Foo]] =
fooQuery(id)
def run[T](query: DBIO[T]): Future[T] =
db.run(query.transactionally)
The services compose the DBIOs and finally call the repository to run the query:
def foonicate(id: Long): Future[Foo] = {
val query = fooRepository.findById(id).flatMap { optFoo =>
val foo: Foo = optFoo match {
case Some(foo) => [business logic finally returning Foo]
case None => [business logic finally returning Foo]
}
fooRepository.save(foo)
}
fooRepository.run(query)
}
That seems to work, but now I can only spec it like this:
val findFooDbio = DBIO.successful(None))
val saveFooDbio = DBIO.successful(Foo(3))
fooRepository.findById(3) returns findFooDbio
fooRepository.save(Foo(3)) returns saveFooDbio
fooRepository.run(any[DBIO[Foo]]) returns Future(Foo(3))
That any in the run mock sucks! Now I'm not testing the actual logic but instead accept any DBIO[Foo]! I've tried to use the following:
fooRepository.run(findFooDbio.flatMap(_ => saveFooDbio)) returns Future(Foo(3))
But it breaks with java.lang.NullPointerException: null, which is specs2's way of saying "sorry mate, the method with this parameter wasn't found". I've tried various variations, but none of them worked.
I suspect that might be because functions can't be compared:
scala> val a: Int => String = x => "hi"
a: Int => String = <function1>
scala> val b: Int => String = x => "hi"
b: Int => String = <function1>
scala> a == b
res1: Boolean = false
Any ideas how to spec DBIO composition without cheating?
I had a similar idea and also investigated it to see if I could:
create the same DBIO composition
use it with matcher in mocking
However I found out that it is actually infeasible in practice:
as you noticed, you cannot compare functions
additionally, when you investigate internals of DBIO, it is basically Free-monad-like structure - it has implementation for plain values and directly generated queries (then, if you extract the statements you could compare some part of the query), but there are also mappings which stores functions
even if you somehow managed to reuse functions, so that they had reference equality, implementations of DBIO do not care about overriding equals, so they would be different beast anyway
So knowing that, I gave up the initial idea. What I can recommend instead:
mock on any input of database.run - it is more error prone, as it won't notify you if test expectations start differing from returned results, but it's better than nothing
replace DBIO by some intermediate structure, that you know you can compare safely. E.g. Cats' Free monad implementation uses case classes so as long as you manage to ensure that functions are somehow comparable (e.g. by not creating them ad hoc, but instead using vals and objects), you could compare on intermediate representation, and mock whole interpret -> run process
replace unit tests with mocked database with integration tests with an actual database
try out Typed Tagless Final Interpreter pattern for handling databases - and basically inject in tests different monad than in production (e.g. prod -> service returning DBIO, production -> service returning Futures you want)
Actually, you could try many other things with Free, TTFI and swapping implementations. The bottom line is - you cannot reliably compare on DBIO, so design your code in a way, that you could test without doing so. It's not a pleasant answer, especially if you just wanted to put together test, and move on, but AFAIK there is no other way.
I have a case class Application that has some input ports, and each port has a name. Then I have another case class that assigns values to the ports of an application.
case class Port (id: ObjectId, name: String, PortType: String)
case class Application (id: ObjectId, ports: List[Port])
case class AppRun (appId: ObjectId, assignments: List[Assignment])
case class Assignment (portName: String, value: String, valueType: String)
I have the applications and their port information in a database, and I get as input an AppRun. I need to make a list of PortValue type (below) showing the value assigned to each port (and the matching is done on port names):
case class PortValue (portId: ObjectId, value: String)
There are a few things that may fail during this matching: application id is invalid, ports do not match, etc. It feels natural to me to write the straightforward algorithm and then catch all exceptions, but that seems Java-ish. On the other hand, I cannot think of a neat way of dealing with Options , checking them one by one, which will obfuscate the code.
The question is how would you solve this Scala way?
EDIT: I need to send a proper message back when such a mismatch happens, like "application not found", etc.
A way to deal with checking Options one by one is to use a for-comprehension. And if you want to keep track of errors you can quite often replace Option with some class that does error-tracking. The common possibilities include:
scala.util.Try[T]. Try is either a Success(result), or a Failure(error: Throwable). It is a built-in class in Scala, and it is simple to combine it with or replace it by scala.concurrent.Future if the need arises.
scala.util.Either[E, T]. Creating a Throwable for every error may not be very efficient because of the need to build the stacktrace. So Either is useful if the error can be a simple String or some application-specific class without the stacktrace. The convention is to have a Right(result) or a Left(error). The downsides are that it's not semantic to have 'right' mean 'success' and 'left' mean 'error', and when you use it in a for-comprehension or call e.g. map method on it, you have to specify whether you want either.right or either.left.
scalaz.\/[E, T] This is the same as Either, but the default for map and for-comprehension is its right side (\/-). Also scalaz provides very useful functions sequence and traverse (see the code below).
scalaz.Validation[Errors, T] or scalaz.ValidationNel[E, T]. Adds a very useful functionality of collecting all the errors, but has slight problems when used in for-comprehensions.
Here is some sample code for your problem, using Try:
import scala.util.{Try, Success, Failure}
def getApplication(appId: ObjectId): Option[Application] = ???
/** Convert Option to Try, using a given failure in case of None */
def toTry[T](option: Option[T])(failure: => Throwable): Try[T] =
option.fold[Try[T]](Failure(failure))(Success(_))
/** Convert a List of Try to a Try of List.
* If all tries in the List are Success, the result is Success.
* Otherwise the result is the first Failure from the list */
def sequence[T](tries: List[Try[T]]): Try[List[T]] =
tries.find(_.isFailure) match {
case Some(Failure(error)) => Failure(error)
case _ => Success(tries.map(_.get))
}
def traverse[T, R](list: List[T])(f: T => Try[R]): Try[List[R]] =
sequence(list map f)
def portValues(task: AppRun): Try[List[PortValue]] = for {
app <- toTry(getApplication(task.appId))(
new RuntimeException("application not found"))
portByName = app.ports.map(p => p.name -> p).toMap
ports <- traverse(task.assignments) { assignment =>
val tryPort = toTry(portByName.get(assignment.portName))(
new RuntimeException(s"no port named ${assignment.portName}"))
tryPort.map(port => PortValue(port.id, assignment.value))
}
} yield ports
Some considerations:
Provided implementations of toTry, sequence and traverse are just a sample. For one, I'd define them in implicit classes to be able to call them like normal methods (e.g. option.toTry(error), or list.traverse(f)).
traverse can be implemented more effectively (stop after the first error is found).
this sequence implementation would return only the first erroneous port.
I prefer API like def getApplication(id: ObjectId): Try[Application] instead of an Option result, because you usually want to have the same error in every part of the code that calls it, and it may give different errors as well (e.g., id not found or network error). If you have def getApplication(id: ObjectId): Application that may throw an error you can simply wrap it in Try: for { app <- Try(getApplication(id)) ...
I am writing a helper object in Scala in which I have high level functions to click elements and such, those functions also throw an NoSuchElementException if the required element doesn't exist. I'm doing this mainly to reuse code.
I've got another class (I'm using Cucumber, so this class has the step definitions) wich calls the the other object functions.
The problem is that those functions expect to receive an Option[Element] with the Element coming from org.scalatest.selenium.WebBrowser.Element, but when I use the finders I get an Element from MyStepClass.this.Element.
Here is a part of my code where I'm getting this problem
class CucumberSteps extends ScalaDsl with EN with Matchers with Firefox {
...
When("""^I click the button with id "([^"]*)"$""") { (buttonId: String) =>
SeleniumUtils.clickOn(find(id(buttonId)))
}
...
}
object SeleniumUtils extends Matchers with Firefox {
...
def clickOn(elem: Option[Element]) {
applyElement(elem, x => click on (x))
}
def applyElement(elem: Option[Element], f: Element => Unit) {
elem match {
case Some(e) => f(e)
case _ => throw new NoSuchElementException
}
}
...
}
I'm not convinced your issue is within SeleniumUtils. The class of the wrapped element returned by ScalaTest's find method is a subclass of org.scalatest.selenium.WebBrowser.Element, and should be successfully matched in your pattern matching statement.
I would try discarding your clickOn utility method and just directly use the ScalaTest Selenium DSL from your test method:
...
click on find(buttonId)
...
This at the very least will identify one way or the other whether your problem was within SeleniumUtils:
If your test passes or progresses further, then I'm wrong and your problem is indeed within your pattern matching code.
If your test fails with the same problem (which I assume is a NoSuchElementException), then I'm right and the explanation is a little simpler (you're trying to find a non-existent ID).