Scala, Specs2 and shared state - scala

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()
}
}
}

Related

mocking method inside another method scala

I am having a problem while mocking a method that is being called in another method.
For example: Below in my main class.
class Trial extends TrialTrait {
def run(): String ={
val a = createA()
val b = a.split(" ")
val c = b.size
val d = c + " words are there"
d
}
def createA(): String = {
var a = "above all the things that have been done, one thing remained in silent above all the things that have been done one thing remained in silent above all the that "
a
}
}
Below is my mock code.
class TryMock4 extends FunSuite with BeforeAndAfterEach with MockFactory {
val trial = new Trial
val st = stub[TrialTrait]
test("Mocking the DataFrame") {
val input = "above all the things that have been done, one thing remained in silent above "
(st.createA _).when().returns(input)
val expected = "14 words are there"
val actual = st.run()
Assert.assertEquals(expected,actual)
}
}
What I am trying to do is, passing mocking data to createA and use that in the run method.
But, it is giving null value after running the run method.
Could you please suggest how it can be achieved?
I don't think you need a mock in this case, just a regular override should suffice.
class TrialTest extends FlatSpec with Matchers {
behavior of "Trial"
it should "count words" in {
val input = "above all the things that have been done, one thing remained in silent above "
val trial = new Trial {
override def createA(): String = input
}
val expected = "14 words are there"
val actual = trial.run()
actual should be (expected)
}
}
However, should you really want to use a mock here, it's possible with scalamock.
You can define our own class that makes part of the class final (the bit you don't want to mock), see below:
class TrialTestWithMock extends FlatSpec with Matchers with MockFactory {
behavior of "Trial"
it should "count words" in {
val input = "above all the things that have been done, one thing remained in silent above "
class FinalTrial extends Trial {
final override def run(): String = super.run()
}
val trial = mock[FinalTrial]
(trial.createA _).expects().returning(input).anyNumberOfTimes()
val expected = "14 words are there"
val actual = trial.run()
actual should be (expected)
}
}

Scalatest: should be Matchers with extra description?

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)
}
}
}
}

ScalaTest test name without fixture?

First off, I saw it and this other post sounds exactly like what I need except for one thing, I can't use fixture.TestDataFixture because I can't extend fixture.FreeSpecLike, and I am sure that there must be some way to get the test name in a way that looks more like this (imagined code that doesn't compile)
class MySpec extends FlatSpecLike with fixture.TestDataFixture {
"this technique" - {
"should work" in {
assert(testData.name == "this technique should work")
}
"should be easy" in { td =>
assert(testData.name == "this technique should be easy")
}
}
}
Any ideas? I just can't believe something like this is not possible :D
While you already came to basically this solution, here is a safer variation:
private val _currentTestName = new ThreadLocal[String]
override def withFixture(test: NoArgTest) = {
_currentTestName.set(test.name)
val outcome = super.withFixture(test)
_currentTestName.set(null)
outcome
}
protected def currentTestName: String = {
val testName = _currentTestName.get()
assert(testName != null, "currentTestName should only be called in a test")
testName
}
Alternately,
protected def currentTestName = Option(_currentTestName.get())
You can find sequence of test names using method testNames in FlatSpecLike trait:
import org.scalatest.FlatSpecLike
class FooSpec extends FlatSpecLike {
it should "check case one" in {
println("test some code ...")
println(testNames.mkString("\n"))
/*
prints:
should check case one
should check case two
*/
// ...
assert(1 == 1)
println("end.")
}
it should "check case two" in {
println("test some code ...")
assert(1 == 1)
println("end.")
}
}
and find each you needed. Hope it helps.
And found an answer(well a collegue did), not sure I like it but works:
on the trait that other tests depend on
class MySpec extends FlatSpecLike {
//... other stuff
var testName = "UndefinedTestName"
override def withFixture (test: NoArgTest) :Outcome= {
testName = test.name
super.withFixture(test)
}
}
simple solution but rather obscure, also I wonder if anyone sees any problems with it

NPE when accessing val that is not lazy

EDIT2:
So another heads up on this:
I still have no idea why this happens, but I have now a similar problem with jOOQ and the Dialect I have to it. My code here looks like this:
object MyDB {
private lazy val dialect = SQLDialect.POSTGRES
def withSession[T](f: DSLContext => T) = f(DSL.using(getConnectionPool, dialect))
}
if I remove the "lazy" it blows up when I try to execute jOOQ queries in line 552 of https://github.com/jOOQ/jOOQ/blob/version-3.2.0/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java
That happens to be a line where the dialect is evaluated. After I added the lazy everything works as expected.
Maybe this is an issue with the threading of LiftWeb and the executing thread does not see the correct value of the val? I have no idea...
EDIT:
I have found a way to do what I want simply by adding a lazy to the values in the first, broken version. So with lazy vals it all works well.
However I'll let this stay open, as I have no idea how to explain this behavior.
Original Post:
So I am trying to use Parameterized Queries in Slick.
My code is below, my problem is that I get an NPE (see comments) when I try to run this from within the webapplication (liftweb, container started with sbt) (the application creates an object of the class PlayerListCollector that is given the string "cola")
When I execute the object as App from within Eclipse the println at the bottom works just fine.
class PlayerListCollector(term: String) {
import PlayerListCollector._
val searchResult = executeSearch(term)
}
object PlayerListCollector extends Loggable with App{
private val searchNameCurrent = Parameters[String].flatMap {
case (term) => {
for {
p <- Players if p.uberName.isNotNull
n <- p.displayName if (n.displayName.toLowerCase.like(term))
} yield (p.id, n.displayName)
}
}
private def executeSearch(term: String) = {
val lowerTerm = "%"+term.toLowerCase()+"%"
logger info "HELLO " +lowerTerm // prints HELLO %cola%
val foo = searchNameCurrent(lowerTerm) // NPE right in this line
logger info foo // never executed from here on ...
val byCurrent = foo.list
logger info byCurrent
[...]
}
// this works if run directly from within eclipse!
println(DB withSession {
searchNameCurrent("%cola%").list
})
}
The problem vanishes when I change the code to look like this:
[...]
object PlayerListCollector extends Loggable with App{
private def executeSearch(term: String) = {
val searchNameCurrent = Parameters[String].flatMap {
case (term) => {
for {
p <- Players if p.uberName.isNotNull
n <- p.displayName if (n.displayName.toLowerCase.like(term))
} yield (p.id, n.displayName)
}
}
val lowerTerm = "%"+term.toLowerCase()+"%"
logger info "HELLO " +lowerTerm // prints HELLO %cola%
val foo = searchNameCurrent(lowerTerm) // executes just fine when the query is in a local val
logger info foo
val byCurrent = foo.list
logger info byCurrent // prints expected output
[...]
}
[...]
}
I have no idea whatsoever why this happens.
Isn't the whole point of a paramterized query to put it in a val that is only once filled with a value so it does not need to be compiled multiple times?
So it turns out I used the App-Trait (http://www.scala-lang.org/api/current/index.html#scala.App) on these objects.
Reading the big fat caveat tells us what is happening I guess.

How to test snippet code in liftweb using specs2 framework

I'm trying to write a Specs2 test case that will test the snippets.
My snippet would look something like this:
class RegisterTest extends Specification {
val testurl = "http:/html/register?username=liftvalues"
val testSession = MockWeb.testS(testurl) { S.session }
def inSession[T](a: => T): T = S.initIfUninitted(testSession) { a }
def is = s2""" example1 $e1 """
val html = <form><input name="username" value="liftvalues"></input></form>
def e1 = {
inSession{
register(html)
}
}
def register(in:NodeSeq):Result = {
val username = S.param("username") //Here we are getting "Empty Value" for the S object.
username === "liftvalues" and UserSchemaTest.registerData("data")
}
}
This test fails since S.param is Empty. What should I do to supply the snippet with a mocked Request?
So far I have looked at Unit Testing Snippets With A Logged In User
and Mocking HTTP Requests, but I do not understand how to achive my goal.
Your code as-is shouldn't even compile, since among other things testSession would return a Box[LiftSession] and S.initIfUninitted requires an unboxed LiftSession. Also, that shouldn't even be needed since MockWeb.testS will initialize the session for you, see here.
I'm not super familiar with Specs2, but I believe something like this should do what you want or at least get you close:
class RegisterTest extends Specification {
val testurl = "http://html/register?username=liftvalues"
val html = <form><input name="username" value="liftvalues"></input></form>
def e1 = register(html)
def register(in:NodeSeq):Boolean = {
val username = S.param("username") //Here we are getting "Empty Value" for the S object.
username === "liftvalues" and UserSchemaTest.registerData("data")
}
MockWeb.testS(testurl) {
s2""" example1 $e1 """
}
}
Everything that gets called from within the MockWeb.testS block should have access to your session and request - so you'd be able to make your method calls normally.
Also, your test also looks wrong, a s2""" will probably throw an error. But, I'm not entirely sure what you are wanting it to do so I couldn't suggest an alternative.