I am trying to implement a test with ScalaTest (WordSpecLike, MustMatchers) that verify if an Either[Seq[Error], Seq[Value]] contains one value and if the value has some specific attributes.
Ideally I would like to have something like:
val result:Either[Seq[Error], Seq[Value]] = ...
result.value must contain { _ have (
'prop ("value")
)}
But this does not compile and I have no clue on how to achieve this kind of matching.
Is there documentation on deep tests or some best practices for that?
Inspectors enable assertions to be made about collections, and in combination with EitherValues, as suggested by #LuisMiguelMejíaSuárez, and checking arbitrary properties with have, the following syntax is possible
atLeast(1, result.right.value) must have ('prop ("value"))
Here is a working example
class Value(val foo: String, val bar: Int) {
def isTooLong: Boolean = foo.length > 2
}
class StackOverflowSpec extends WordSpec with MustMatchers with EitherValues with Inspectors {
"Result" when {
"Right" should {
"have property" in {
val result: Either[Seq[Error], Seq[Value]] = Right(Seq(new Value("aa", 11), new Value("bbb", 42)))
atLeast(1, result.right.value) must have ('tooLong (true), 'bar (42) )
}
}
}
}
Alternatively try pattern matching on result and passing the property predicate to Seq.exists like so
class Value(val foo: String, val bar: String) {
def isTooLong: Boolean = foo.length > 2
}
class StackOverflowSpec extends WordSpec with MustMatchers {
"Result" when {
"Right" should {
"have property" in {
val result: Either[Seq[Error], Seq[Value]] = Right(Seq(new Value("a", "b"), new Value("ccc", "ddd")))
result match {
case Left(seq) => fail
case Right(seq) => seq.exists(_.isTooLong) must be(true)
}
}
}
}
}
Related
Consider the following Table:
object MyTestsFactory {
val testCases = Table(
("testName", "input", "output"),
("test-1", "1", 1),
("test-2", "2", 2),
("test-3", "3", 3),
)
}
and the following tests case:
class MySpec extends FunSuite {
test("Test Parsing Function") {
forAll(MyTestsFactory.testCases) { (testName: String, input: String, output: Int) =>
input.toInt shouldBe output
}
}
}
Notice that it creates one test, and runs all the inputs on the specific test.
Is that possible to create test for each row on the Table via forAll? is there maybe other solution? (C# solution attached)
For example something as follows:
class MySpec extends FunSuite {
test("Test Parsing Function") {
forAll(MyTestsFactory.testCases) { (testName: String, input: String, output: Int) =>
test(s"Testing $testName") {
input.toInt shouldBe output
}
input.toIntOption match {
case Some(value) => value shouldBe output
case None => fail(s"Parsing error on $testName")
}
}
}
}
which does not compile
TestRegistrationClosedException was thrown during property evaluation. (ConsentStringSpec.scala:12)
Message: A test clause may not appear inside another test clause.
Location: (ConsentStringSpec.scala:13)
Occurred at table row 0 (zero based, not counting headings), which had values (
testName = test-1,
input = 1,
output = 1
)
Is it possible in some way?
Example on C#: (Looking for something similar in Scala)
public class MySpec
{
[TestCase( "1", 1, TestName = "test-1")]
[TestCase("2", 2, TestName = "test-2")]
[TestCase("3", 3, TestName = "test-3")]
public void TestingParsing(string input, int output)
{
Assert.AreEqual(int.Parse(input), output);
}
}
The problem with your code, is that you are trying to nest your tests. It cannot be nested. Please just try to flip the test and forall order, for example:
class MySpec extends FunSuite with Matchers {
forAll(MyTestsFactory.testCases) {
x => {
test("Test Parsing Function" + x._1) {
x._2.toInt shouldBe x._3
}
}
}
}
Then you get 3 tests:
Trying to do some TDD while learning the playframework:
class ContentFetching extends PlaySpec with BeforeAndAfter with MockFactory
{
private val service = ContentService
private val languages = List(Language(Some(1), "en", "English", "Placeholder"),
Language(Some(2), "de", "Deutsch", "Platzhalter")
)
"find" must
{
"fail if languageCode is invalid" in
{
val fakeRepository = mock[LanguageRepositoryTrait]
(fakeRepository.get _).expects().returning(languages)
fakeRepository.get must have length 3
service.find("fr") must be Result.NotFound
}
}
}
ContentService would call:
def fourOhFour() = NotFound(s"Oops, content could not be found")
yet the assertion service.find("fr") must be Result.NotFound won't compile. Why is that and how to test this then?
Here is the whole contentService (as requested) Sadly it extends Controller currently, because I found no other way to return an Action. Actually I use MVC, but a Service + repository-layer as well:
class ContentServiceComponent(languageRepository: LanguageRepositoryTrait, nodeRepository: NodeRepositoryTrait) extends Controller
{
def find(language: String) = Action
{
languageRepository.get().map(l => l.code).contains(language) match
{
case true => Ok(s"Homepage for $language")
case false => fourOhFour()
}
}
def fourOhFour() = NotFound(s"Oops, content could not be found")
}
object ContentService extends ContentServiceComponent(LanguageRepository, NodeRepository)
service.find("fr") from the Play controller returns a Result
Maybe you're comparing a Result with Result.Status type?
Can you use a play.api.helper?
status(of: Future[Result]) = Await.result(of, timeout.duration).header.status
Instead of
service.find("fr") must be Result.NotFound
Try something like this?
status(service.find("fr")) must be NOT_FOUND
NOT_FOUND is a play.api.http.Status value and just maps to the standard HTTP status code 404
find instead returns Action could return Option
def find(language: String): Option[String] =
{
languageRepository.get().map(l => l.code).contains(language) match
{
case true => Some(s"Homepage for $language")
case _ => None
}
}
So you cound handle it at Controller as follow:
def index(language:String) = Action{
ContentService.find(language) match{
case Some(content) => Ok(content)
case _ => NotFound(s"Oops, content could not be found")
}
}
and you could do test case something like this:
class ContentFetching extends PlaySpec with BeforeAndAfter with MockFactory
{
val languages = List(Language(Some(1), "en", "English",Placeholder"),Language(Some(2), "de", "Deutsch", "Platzhalter")
)
val languagesRepositoryMockHere = new LanguageRepositoryTrait{
override def get = languages //I don't know the implementations
}
"find" must
{
"fail if languageCode is invalid" in
{
private val service = new ContentServiceComponent(languagesRepositoryMockHere,nodeRepositoryMockHere)
service.find("fr") shouldBe None
}
}
}
This was not tested but could help for reference.
Cheers.
I wrote some Scala code, using reflection, that returns all vals in an object that are of a certain type. Below are three versions of this code. One of them works but is ugly. Two attempts to improve it don't work, in very different ways. Can you explain why?
First, the code:
import scala.reflect.runtime._
import scala.util.Try
trait ScopeBase[T] {
// this version tries to generalize the type. The only difference
// from the working version is [T] instead of [String]
def enumerateBase[S: universe.TypeTag]: Seq[T] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[T])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
}
trait ScopeString extends ScopeBase[String] {
// This version works but requires passing the val type
// (String, in this example) explicitly. I don't want to
// duplicate the code for different val types.
def enumerate[S: universe.TypeTag]: Seq[String] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
// This version tries to avoid passing the object's type
// as the [S] type parameter. After all, the method is called
// on the object itself; so why pass the type?
def enumerateThis: Seq[String] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[this.type].decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
}
// The working example
object Test1 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerate[Test1.type]
}
// This shows how the attempt to generalize the type doesn't work
object Test2 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateBase[Test2.type]
}
// This shows how the attempt to drop the object's type doesn't work
object Test3 extends ScopeString {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateThis
}
val test1 = Test1.fields // List(test)
val test2 = Test2.fields // List(13, test)
val test3 = Test3.fields // List()
The "enumerate" method does work. However, as you can see from the Test1 example, it requires passing the object's own type (Test1.type) as a parameter, which should not have been necessary. The "enumerateThis" method tries to avoid that but fails, producing an empty list. The "enumerateBase" method attempts to generalize the "enumerate" code by passing the val type as a parameter. But it fails, too, producing the list of all vals, not just those of a certain type.
Any idea what's going on?
Your problem in your generic implementation is the loss of the type information of T. Also, don't use exceptions as your primary method of control logic (it's very slow!). Here's a working version of your base.
abstract class ScopeBase[T : universe.TypeTag, S <: ScopeBase[T, S] : universe.TypeTag : scala.reflect.ClassTag] {
self: S =>
def enumerateBase: Seq[T] = {
val mirror = currentMirror.reflect(this)
universe.typeOf[S].baseClasses.map(_.asType.toType).flatMap(
_.decls
.filter(_.typeSignature.resultType <:< universe.typeOf[T])
.filter(_.isMethod)
.map(_.asMethod)
.filter(_.isAccessor)
.map(decl => mirror.reflectMethod(decl).apply().asInstanceOf[T])
.filter(_ != null)
).toSeq
}
}
trait Inherit {
val StringField2: String = "test2"
}
class Test1 extends ScopeBase[String, Test1] with Inherit {
val IntField: Int = 13
val StringField: String = "test"
lazy val fields = enumerateBase
}
object Test extends App {
println(new Test1().fields)
}
Instead of getting the type from universe.typeOf you can use the runtime class currentMirror.classSymbol(getClass).toType, below is an example that works:
def enumerateThis: Seq[String] = {
val mirror = currentMirror.reflect(this)
currentMirror.classSymbol(getClass).toType.decls.map {
decl => Try(mirror.reflectField(decl.asMethod).get.asInstanceOf[String])
}.filter(_.isSuccess).map(_.get).filter(_ != null).toSeq
}
//prints List(test)
With everyone's help, here's the final version that works:
import scala.reflect.runtime.{currentMirror, universe}
abstract class ScopeBase[T: universe.TypeTag] {
lazy val enumerate: Seq[T] = {
val mirror = currentMirror.reflect(this)
currentMirror.classSymbol(getClass).baseClasses.map(_.asType.toType).flatMap {
_.decls
.filter(_.typeSignature.resultType <:< universe.typeOf[T])
.filter(_.isMethod)
.map(_.asMethod)
.filterNot(_.isConstructor)
.filter(_.paramLists.size == 0)
.map(decl => mirror.reflectField(decl.asMethod).get.asInstanceOf[T])
.filter(_ != null).toSeq
}
}
}
trait FieldScope extends ScopeBase[Field[_]]
trait DbFieldScope extends ScopeBase[DbField[_, _]] {
// etc....
}
As you see from the last few lines, my use cases are limited to scope objects for specific field types. This is why I want to parameterize the scope container. If I wanted to enumerate the fields of multiple types in a single scope container, then I would have parameterized the enumerate method.
I have some classes which sometimes have many many attributes, the classes are very large, so I don't want to turn the class into a case class.
However, I still want to be able to do a pattern match on the class type.
What I have been doing is the following:
object CourseSemester {
implicit val courseSemesterCase = (entity: CourseSemester)
=> { CourseSemesterCase(entity) }
case class CourseSemesterCase(entity: CourseSemester)
}
import CourseSemester._
class CourseSemester(val courses: List[Course],
val startDate: EventDate,
val endDate: EventDate,
val createdUpdatedBy: CreatedUpdatedBy,
... there are so many attributes... ) {
def totalCoursesInSemester: Int = courses.length
}
This allows me to do a match on a CourseSemester to the case class, so I can identify the class type in my pattern match. For example:
val c = new CourseSemester(...)
c match {
case CourseSemesterCase(a) => { }
case SomeOtherCase(b) => { }
}
Is this a reasonable way to do it, or is there a better way?
You may use Type Ascription
c match {
case cs : CourseSemester => // use cs
case s : SomeOther => // s is object of SomeOther type
}
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
}