Is there some preferred way to design a Specs2 test, with lots of tests that depend on the results of previous tests?
Below, you'll find my current test suite. I don't like the vars inbetween the test fragments. They're "needed" though, since some tests generate ID numbers that subsequent tests reuses.
Should I perhaps store the ID numbers in a Specs2 Context instead, or create a separate Object that holds all mutable state? And place only test fragments in the specification object? Or is there some even better approach?
If a test fails, I'd like to cancel the remaining test at the same depth. Can I make the test fragments depend upon each other? (I know I can cancel remaining matchers in a single test fragment (by using mutable tests, or via orSkip), but what about cancelling whole fragments?)
.
object DatabaseSpec extends Specification {
sequential
"The Data Access Object" should {
var someId = "" // These var:s feels error prone, is there a better way?
"save an object" >> {
someId = database.save(something)
someId must_!= ""
// I'd like to cancel the remaining tests, below, at this "depth",
// if this test fragmen fails. Can I do that?
// (That is, cancel "load one object", "list all objects", etc, below.)
}
"load one object" >> {
anObject = database.load(someId)
anObject.id must_== someId
}
"list all objects" >> {
objs = database.listAll()
objs.find(_.id == someId) must beSome
}
var anotherId = ""
...more tests that create another object, and
...use both `someId` and `anotherId`...
var aThirdId = ""
...tests that use `someId`, `anotherId` and `aThirdId...
}
"The Data Access Object can also" >> {
...more tests...
}
}
There are 2 parts to your question: using vars for storing intermediary state, and stopping examples when one is failing.
1 - Using vars
There are some alternatives to using vars when using a mutable specification.
You can use lazy vals representing the steps of your process:
object DatabaseSpec extends mutable.Specification {
sequential
"The Data Access Object" should {
lazy val id1 = database.save(Entity(1))
lazy val loaded = database.load(id1)
lazy val list = database.list
"save an object" >> { id1 === 1 }
"load one object" >> { loaded.id === id1 }
"list all objects" >> { list === Seq(Entity(id1)) }
}
object database {
def save(e: Entity) = e.id
def load(id: Int) = Entity(id)
def list = Seq(Entity(1))
}
case class Entity(id: Int)
}
Since those values are lazy, they will only be called when the examples are executed.
If you're ready to change the structure of your current specification you can also use the latest 1.12.3-SNAPSHOT and group all those small expectations into one example:
"The Data Access Object provides a save/load/list api to the database" >> {
lazy val id1 = database.save(Entity(1))
lazy val loaded = database.load(id1)
lazy val list = database.list
"an object can be saved" ==> { id1 === 1 }
"an object can be loaded" ==> { loaded.id === id1 }
"the list of all objects can be retrieved" ==> {
list === Seq(Entity(id1))
}
}
If any of those expectations fail then the rest will not be executed and you will get a failure message like:
x The Data Access Object provides a save/load/list api to the database
an object can not be saved because '1' is not equal to '2' (DatabaseSpec.scala:16)
Another possibility, which would require 2 small improvements, would be to use the Given/When/Then way of writing specifications but using "thrown" expectations inside Given and When steps. As you can see in the User Guide, the Given/When/Then steps extract data from strings and pass typed information to the next Given/When/Then:
import org.specs2._
import specification._
import matcher.ThrownExpectations
class DatabaseSpec extends Specification with ThrownExpectations { def is =
"The Data Access Object should"^
"save an object" ^ save^
"load one object" ^ load^
"list all objects" ^ list^
end
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
1
}
val load: When[Int, Int] = groupAs(".*") and { (id: Int) => (s: String) =>
val e = database.load(id)
e.id === 1
e.id
}
val list: Then[Int] = groupAs(".*") then { (id: Int) => (s: String) =>
val es = database.list
es must have size(1)
es.head.id === id
}
}
The improvements, which I'm going to do, are:
catch failure exceptions to report them as failures and not errors
remove the necessity to use groupAs(".*") and when there's nothing to extract from the string description.
In that case it should be enough to write:
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
1
}
Another possibility would be to allow to directly write:
val save: Given[Int] = groupAs(".*") and { (s: String) =>
database.save(Entity(1)) === 1
}
where a Given[T] object can be created from a String => MatchResult[T] because the MatchResult[T] object already contains a value of type T, that would become a "Given".
2 - Stop the execution after a failing example
Using the implicit WhenFail Around context is certainly the best way to do what you want (unless you go with the expectations descriptions as shown above the G/W/T example).
Note on step(stepOnFail = true)
The step(stepOnFail = true) works by interrupting the following examples if one example in the previous block of concurrent examples failed. However, when you're using sequential, that previous block is limited to just one example. Hence what you're seeing. Actually I think that this is a bug and that all the remaining examples should not be executed, whether you're using sequential or not. So stay tuned for a fix coming up this week-end.
(Concerning question 1: I don't know if there's some better alternative to the vars inside the examples. Perhaps my examples are simply too long, and perhaps I should split my Spec:s into many smaller specs.)
Concerning question 2, I found in this email by etorreborre that stopping subsequent tests can be done like so:
"ex1" >> ok
"ex2" >> ok
"ex3" >> ko
step(stopOnFail=true)
"ex4" >> ok
(Ex4 will be skipped if ex1, ex2 or ex3 fails. (This doesn't work as expected in Specs2 < 1.12.3 if you're using a sequential spec, however.))
Here's another way: According to this Specs2 Googl groups email by etorreborre one can have subsequent tests stop on failure, like so:
("example2" would be skipped, but "example3" and "4" would run)
class TestSpec extends SuperSpecification {
sequential
"system1" >> {
implicit val stop = WhenFail()
"example1" >> ko
"example2" >> ok
}
"system2" >> {
implicit val stop = WhenFail()
"example3" >> ok
"example4" >> ok
}
}
case class WhenFail() extends Around {
private var mustStop = false
def around[R <% Result](r: =>R) = {
if (mustStop) Skipped("one example failed")
else if (!r.isSuccess) { mustStop = true; r }
else r
}
}
In this email by etorreborre there's a method to cancel subsequent specifications if an example fails, if you've include a list of specifications:
sequential ^ stopOnFail ^
"These are the selenium specifications" ^
include(childSpec1, childSpec2, childSpec3)
And you'd need to edit test options in build.sbt so the child specs aren't executed again indepentendly after they've been included. From the email:
testOptions := Seq(Tests.Filter(s =>
Seq("Spec", "Selenium").exists(s.endsWith(_)) &&
! s.endsWith("ChildSpec")))
Specs doc states that you may use .orSkip to skip the rest of the example in case of a failure
"The second example will be skipped" >> {
1 === 2
(1 === 3).orSkip
}
But I have not tried it personally
Related
To test Scala Future value I use this code :
"Simple Test" should "Test Future" in {
val futureData1: Future[Data] = MakeData()
futureData1 map { data => {
assert(data.value == 2)
}
}
}
This behaves as expected, how to test values that are contained in two Futures ?
For example, how to correctly modify below that tests two Futures:
"Simple Test" should "Test Future" in {
val futureData1: Future[Data] = MakeData()
val futureData1: Future[Data] = MakeData()
futureData1 map { data => {
futureData1 map { data2 => {
assert(data.value == data2.value)
}
}
}
}
}
Reading https://www.scalatest.org/user_guide/async_testing , there does not appear to be documentation describing the testing of multiple Futures.
Using map like this is unlikely to work the way you want it to - the assert inside map does not get run until the future is complete, which will likely happen after the test has already finished, and even if not, the exception from assert will be wrapped into a future and never actually thrown on the main thread, so the test will always pass.
You seem to be using scalatest, so, it probably makes sense to mix in Eventually so that you can do these assertions property:
val f = doStuff()
eventually {
f.value shouldBe Some(Data(2))
}
Or, if you prefer ScalaFutures semantics (mix in ScalaFutures instead of Eventually), you can do:
val f = doStuff()
whenReady(f) { data => assert(data.value == 2) }
Or just
assert(doStuff().futureValue.value == 2)
That also makes the answer to your question quite obvious:
assert(future1.futureValue == future2.futureValue)
If you are still looking for a way to combine several futures together, you can use .zip (for two futures) or .sequence (for any number):
whenReady(future1 zip future2) { case (d1, d2) => assert(d1 == d2) }
whenReady(Future.sequence(List(f1, f2, f3, f4)) { case first :: rest =>
assert(rest.forall(_ == first))
I am writing some Specs2 specifications; that looks like:
class ComponentSpecification extends Specification with Mockito {
private val dependency = mock[Dependency]
private val subject = new Component(..)
"methodOne" should {
"handle happy path" in {
val result = subject.methodOne("Param1", 42)
result must ...
there was one(dependency).something()
}
"deal with border case" in {
val result = subject.methodOne("", -1)
result must ...
there was one(dependency).something()
}
}
}
However, those tests fails because the mock[Dependency] is shared.
One solution would be to make them sequential and reset the mock before each test but this look odd and as written in the doc about "Parallel by default":
it encourages to write independent examples when the result of a given example should not be influenced by others
Another would be to move the val to the test itself. But while I should be able to reduce the duplication with this still looks like a strange structure. And looks like the subject is stateful while it should not.
I can also try to use a less strict approach by verifying with there was atLestOne(dependency).something() but:
this does not validate that the method was called in this specific test case and
argument capture and validation is painful.
So my question is:
How can I create readable tests with detailed verifications on mock.
Thanks a lot.
Scopes can provide fresh state to each test like so
class ComponentSpecification extends mutable.Specification with Mockito {
trait FooScope extends Scope {
val dependency = mock[Dependency]
val subject = new Component(dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val result = subject.methodOne("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val result = subject.methodOne("", -1)
there was one(dependency).something()
}
}
}
where there is no need to reset the mock before each test.
I was going to accept the answer from #Mario Galic. However, regarding the comment of #Eric (author of Specs2) I ended with a method that creates the context as expected but removes duplication. By using pattern matching I extract the interesting part :
class ComponentSpecification extends mutable.Specification with Mockito {
def givenOneCallToMethodOneWithDependency(s:String, i:Int):(Result, Dependency) = {
val dependency = mock[Dependency]
val subject = new Component(dependency)
val result = subject.methodOne(s, i)
(result, dependency)
}
"methodOne" should {
"handle happy path" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("Param1", 42)
there was one(dependency).something()
}
"deal with border case" in new FooScope {
val (result, dependency) = givenOneCallToMethodOneWithDependency("", -1)
there was one(dependency).something()
}
}
}
Consider the following code:
import chisel3._
import chisel3.util._
import chisel3.iotesters._
class Inverter extends Module {
val io = IO(new Bundle {
val a = Input(UInt(4.W))
val s = Output(UInt(4.W))
})
io.s := ~io.a
}
class InverterTester(c: Inverter) extends PeekPokeTester(c) {
poke(c.io.a, 8)
step(1)
expect(c.io.s, 8) // Should be 7 here
}
object TestMain extends App {
chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
}
Now I run sbt 'test:runMain TestMain' and got this line (info is in purple):
[info] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
And the exit value of sbt is zero.
I need that line to be an error (with red color):
[error] [0.002] EXPECT AT 1 io_s got 7 expected 8 FAIL
As well as making the above sbt command exit with a non-zero value.
How can I achieve it with minimal change to existing code?
First the easy part. You can get a non-zero result code by using the result of chisel.execute like this.
val result = chisel3.iotesters.Driver.execute(args, () => new Inverter()) {
c => new InverterTester(c)
}
System.exit(if(result) 0 else 1)
Changing the logging level, unfortunately requires changing each of the separate backends in the chisel-testers repo. The following is an example of changing the TreadleBackend.scala, one of the three.
def expect(signal: InstanceId, expected: BigInt, msg: => String)
(implicit logger: TestErrorLog, verbose: Boolean, base: Int) : Boolean = {
signal match {
case port: Element =>
val name = portNames(port)
val got = treadleTester.peek(name)
val good = got == expected
if (!good) {
logger error
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
else if (verbose) {
logger info
s"""EXPECT AT $stepNumber $msg $name got ${bigIntToStr(got, base)} expected ${bigIntToStr(expected, base)}""" +
s""" ${if (good) "PASS" else "FAIL"}"""
}
if(good) treadleTester.expectationsMet += 1
good
case _ => false
}
}
This would not be an unreasonable issue to file, I think a logger.error would make more sense on an expect failure. There is some concern that changing this could have unexpected consequences for existing users, who are looking for that string.
But I'd like to encourage you to take a look at freechipsproject/chisel-testers2 repo. It's where the team is putting most of their testing development time. It would be easier to change, it has a lot of other nice features for building unit tests, and we are looking at ways we can make it better than chisel-testers.
I have a org.scalatest.FunSpec with org.scalatest.Matchers test that does the following e.g.
val tol = 1e-10
val res = 1.000000000000001
val ref = 1.000000000000000
res should be (ref +- tol)
but it does so in a loop for multiple cases keyed by name, of course I can't change the granularity of the tested code so I get a collection of values with those names associated to them. Therefore for a test above I need to place an extra context or extra description name to reflect to which name it applies. I need something like:
val name : String = ...
res should be (ref +- tol) for name
I can't use it and describe at this point because they are already outside.
It really depends on what you're trying to do, and you should probably add a more complete example of what you're trying to achieve, but you could use describe in the loop. For example:
class TempTest extends FunSpec with Matchers {
describe("Some example test") {
(1 to 10).foreach { i => // your loop here
describe(s"Scenario $i") {
it("should be equal to itself") {
i shouldBe i
}
}
}
}
}
UPDATE: You could use withClue to add more context to the matcher e.g.:
withClue("Some clarifying message") {
i shouldBe 5
}
This will add the clue string to the error if the conditions fails.
Probably GivenWhenThen can be used in order to add context to the tests reporting. I am not sure how exactly you wrap your multiple tests in the loop, but here is the idea:
import org.scalatest.{GivenWhenThen, WordSpec}
/**
* Created by alex on 10/3/16.
*/
class Temp extends WordSpec with GivenWhenThen{
val names = List("Alex", "Dana")
for(name <- names)yield{
"Reversing a name " + name + " two times" should {
"result in the same name" in{
Given("name " + name)
When("reversed two times")
val reversed = name.reverse.reverse
Then("it should be the same")
assert(name === reversed)
}
}
}
}
I am trying to build a simple RESTful service that performs CRUD operations on a database and returns JSON. I have a service adhering to an API like this
GET mydomain.com/predictions/some%20string
I use a DAO which contains the following method that I have created to retrieve the associated prediction:
def getPrediction(rawText: String): Prediction = {
val predictionAction = predictions.filter{_.rawText === rawText}.result
val header = predictionAction.head
val f = db.run(header)
f.onComplete{case pred => pred}
throw new Exception("Oops")
}
However, this can't be right, so I started reading about Option. I changed my code accordingly:
def getPrediction(rawText: String): Option[Prediction] = {
val predictionAction = predictions.filter{_.rawText === rawText}.result
val header = predictionAction.headOption
val f = db.run(header)
f.onSuccess{case pred => pred}
None
}
This still doesn't feel quite right. What is the best way to invoke these filters, return the results, and handle any uncertainty?
I think the best way to rewrite your code is like this:
def getPrediction(rawText: String): Future[Option[Prediction]] = {
db.run(users.filter(_.rawText === rawText).result.headOption)
}
In other words, return a Future instead of the plain result. This way, the database actions will execute asynchronously, which is the preferred way for both Play and Akka.
The client code will then work with the Future. Per instance, a Play action would be like:
def prediction = Action.async {
predictionDao.getPrediction("some string").map { pred =>
Ok(views.html.predictions.show(pred))
}.recover {
case ex =>
logger.error(ex)
BadRequest()
}
}