I have some specs2 tests with Scope:
"hello" should {
"return world" in new Subject {
hello.get === "world"
}
}
trait Subject extends org.specs2.specification.Scope {
val hello = new Hello
}
Now I want to convert it into acceptance style:
def is = s2"""
hello.get should return 'world' $e1
"""
def e1 = new Subject {
hello.get === "world"
}
trait Subject extends org.specs2.specification.Scope {
val hello = new Hello
}
It seems working and the test is passed, but soon I found it's never failed no matter how I modify it:
hello.get === "invalid-world" // still passing
How to use Scope correctly in acceptance style?
Just found an issue for this problem: https://github.com/etorreborre/specs2/issues/180
The answer quoted from there:
This is to be expected. Scopes are traits which are only intended to be used with mutable
specifications. You have 2 ways around this:
use a case class or case object
s2"""
will fail ${t.fail}
"""
case object t {
val foo = false
def fail = foo must beTrue
}
mix-in the ThrownExpectations trait to the Specification so that failed expectations will "bubble-up"
outside of the Scope trait
Related
I have a HelperMethod class.
class HelperMethods {
def getUniqueID(): UUID = {
UUID.randomUUID()
}
def bucketIDFromEmail(email:String): Int = {
val bucketID= email(0).toInt
println("returning id "+bucketID+" for name "+email)
bucketID
}
}
And an object which has an instance of HelperMethods
package object utilities{
private val helper = new HelperMethods()
def getUniqueID(): UUID = helper.getUniqueID()
def bucketIDFromEmail(email:String): Int = helper.bucketIDFromEmail(email)
}
I wrote a spec to test that my mock works correctly.
class UserControllerUnitSpec extends PlaySpec {
val mockHelperMethods = mock(classOf[HelperMethods])
when(mockHelperMethods.getUniqueID()).thenReturn(UUID.fromString("87ea52b7-0a70-438f-81ff-b69ab9e57210"))
when(mockHelperMethods.bucketIDFromEmail(ArgumentMatchers.any[String])).thenReturn(1)
"mocking helper class " should {
"work" in {
val bucketId = utilities.bucketIDFromEmail("t#t.com")
println("user keys are " + userKeys)
val id: UUID = utilities.getUniqueID()
println("got id " + userKeys)
bucketId mustBe 1
id mustBe UUID.fromString("87ea52b7-0a70-438f-81ff-b69ab9e57210")
}
}
}
the test fails with reason 116 was not equal to 1. This corresponds to line
bucketId mustBe 1 in the spec. I can see the print returning id 116 for name t#t.com. I shouldn't see it as I am trying to mock this class. I suspect that it could be because the utilities object is getting created before the statement val mockHelperMethods = mock(classOf[HelperMethods]) in the spec.
Question 2- Is there a way to mock HelperMethods and make utilities use the mocked class?
You have mocked HelperMethods but not utilities.
Question 2- Is there a way to mock HelperMethods and make utilities use the mocked class?
It is not possible to mock an object.
If you want, you have to extract the behavior in a trait.
Here is a solution that would work:
package utils
// move the behavior to a trait:
trait UtitilitiesTrait {
private[utils] def helper = new HelperMethods()
def getUniqueID(): UUID = helper.getUniqueID()
def bucketIDFromEmail(email: String): Int = helper.bucketIDFromEmail(email)
}
// provide an object for real use
object Utilities extends UtitilitiesTrait
// override helper for test mock
object TestUtilities extends UtitilitiesTrait {
private[utils] override def helper = mock(classOf[HelperMethods])
}
And here is your test:
class UserControllerUnitSpec extends PlaySpec {
val mockHelperMethods = mock(classOf[HelperMethods])
object TestUtilities extends UtitilitiesTrait {
private[utils] override def helper = mockHelperMethods
}
when(mockHelperMethods.getUniqueID()).thenReturn(UUID.fromString("87ea52b7-0a70-438f-81ff-b69ab9e57210"))
when(mockHelperMethods.bucketIDFromEmail(ArgumentMatchers.any[String])).thenReturn(1)
"mocking helper class " should {
"work" in {
val bucketId = TestUtilities.bucketIDFromEmail("t#t.com")
println("user keys are " + userKeys)
val id: UUID = TestUtilities.getUniqueID()
println("got id " + userKeys)
bucketId mustBe 1
id mustBe UUID.fromString("87ea52b7-0a70-438f-81ff-b69ab9e57210")
}
}
}
The typical pattern that enables mocking objects used internally is to inject them, or at least provide a way to inject an alternate.
Since Utilities is an object, you can't inject using a constructor. You could still introduce a setter method.
If you'd like to discourage use of the setter for anything other than unit tests, make it package-private, and you might also prefix the name with "qa":
private[utils] def qaSetHelperMethods(qaHelper: HelperMethods): Unit
In JS, I can access the function arguments through the arguments object.
I want to do the same thing somehow in ScalaJS, because I want to do some logging for functions and what parameters they got.
Is it possible?
Hmm -- interesting question. I honestly don't know if there is a way to access arguments per se. I would probably address the desire for logging the function arguments by using the sourcecode library, which is designed for that sort of thing, but I'll admit that I haven't tried that from Scala.js yet...
You cannot directly access the arguments object in Scala.js. However, you can export a method with varargs and it will work as expected in JavaScript:
object Logger {
#JSExportTopLevel("log")
def log(xs: js.Any*): Unit = {
xs.foreach(println)
}
}
This defines and exports log to the top level scope. In the JavaScript code, you can now call:
log(1, {}, {a: 1}, "foo");
As #justin-du-coeur suggested, you can use sourcecode for this. For example:
object Test extends js.JSApp {
def main(): Unit = {
a(1, "a")
b()
c("foo", "bar", "baz")
}
def trace()(implicit name: sourcecode.Name, args: sourcecode.Args): Unit = {
def makeArgList(as: Seq[sourcecode.Text[_]]): String =
as.map(a => f"${a.source} = ${a.value}").mkString("(", ", ", ")")
val argStr = args.value.map(makeArgList).mkString("")
println(f"${name.value}$argStr")
}
def a(arg1: Int, arg2: String): Unit = {
trace()
}
def b(): Unit = {
trace()
}
def c(x: String*): Unit = {
trace()
}
}
The output is as follows:
[info] Running Test
a(arg1 = 1, arg2 = a)
b()
c(x = WrappedArray(foo, bar, baz))
As you can see, trace can capture everything it needs from the context, so the result is even more boilerplate free than any JS solution I can think of.
I'm trying to run a test with scaldi and specs2. In the test I need to override a StringManipulator function that uses an injected ProxyManipulator. The ProxyManipulator takes a string and returns its upper case in a Future. The replacement manipulator in the test returns a Future("Test Message").
Here is the StringManipulator class where the injection occurs:
class StringManipulator {
def manip (str : String) (implicit inj: Injector) : String = {
val prox = inject[ProxyManipulator]
Await.result(prox.manipulate(str), 1 second)
}
}
I'm using a package.object that contains the implicit injector:
import modules.MyModule
package object controllers {
implicit val appModule = new MyModule
}
And here is the specs2 test with the new binding:
#RunWith(classOf[JUnitRunner])
class StringManipScaldiSpec extends Specification {
class TestModule extends Module {
bind [ProxyManipulator] to new ProxyManipulator {
override def manipulate(name: String) = Future("Test Message")
}
}
"Application" should {
"do something" in {
val myTestModule = new TestModule
val str = "my string"
val stringMan = new StringManipulator() //(myTestModule)
stringMan.manip(str)(myTestModule) === "Test Message"
}
}
}
The problem is that when the test runs the class StringManipulator is still using the original Proxy Manipulator instead of the one passed in the TestModule. Any ideas?
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.
I have a wide array of Hamcrest matchers for my domain objects written in Java.
I'm now moving to Scala and would like to reuse these existing matchers in the context of specs2 tests.
Given a Hamcrest matcher for class Foo:
public class FooMatcher extends TypeSafeMatcher[Foo] {
...
}
I'd like to be able to use it thus:
val myFooMatcher = new FooMatcher(...)
foo must match (myFooMatcher)
foos must contain (myFooMatcher1, myFooMatcher2)
And so on.
Specs2 seems to have the opposite, an adapter of its Matcher[T] trait to org.hamcrest.Matcher, but I'm looking for the other way around.
Any ideas?
You need to add one implicit conversion for this to work:
import org.hamcrest._
import org.specs2.matcher.MustMatchers._
implicit def asSpecs2Matcher[T](hamcrest: org.hamcrest.TypeSafeMatcher[T]):
org.specs2.matcher.Matcher[T] = {
def koMessage(a: Any) = {
val description = new StringDescription
description.appendValue(a)
hamcrest.describeTo(description)
description.toString
}
(t: T) => (hamcrest.matches(t), koMessage(t))
}
Let's see it in action:
case class Foo(isOk: Boolean = true)
// a Hamcrest matcher for Foo elements
class FooMatcher extends TypeSafeMatcher[Foo] {
def matchesSafely(item: Foo): Boolean = item.isOk
def describeTo(description: Description) = description.appendText(" is ko")
}
// an instance of that matcher
def beMatchingFoo = new FooMatcher
// this returns a success
Foo() must beMatchingFoo
// this returns a failure
Foo(isOk = false) must beMatchingFoo
// this is a way to test that some elements in a collection have
// the desired property
Seq(Foo()) must have oneElementLike { case i => i must beMatchingFoo }
// if you have several matchers you want to try out
Seq(Foo()) must have oneElementLike { case i =>
i must beMatchingFoo and beMatchingBar
}