Hi all. I'm working on a
(a platform for writing Android Apps in Scala) and have hit a brick wall trying to put it under test in the most recent version of Specs2 (using the
).
You shouldn't mixin `org.specs2.matcher.MustExpectations` or `org.specs2.matcher.ShouldExpectations` with `Scope`, use `org.specs2.matcher.MustThrownExpectations` or `org.specs2.matcher.ShouldThrownExpectations` instead.
Getting there looks like this:
The test looks like this:
package org.tlc.whereat.services
import com.squareup.okhttp.OkHttpClient
import io.taig.communicator.internal.result.Parser
import org.specs2.mutable.Specification
import org.tlc.whereat.model.{ApiIntersection, Intersection, Loc}
import org.tlc.whereat.msg.IntersectionResponse
import org.tlc.whereat.support.{AppContextTestSupport, BaseTestSupport}
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
trait IntersectionServiceMock
extends IntersectionService
with AppContextTestSupport {
val rcLoc = Loc(40.7206235,-74.0007963)
val rcLocReq = toIntersectionRequest(rcLoc)
val validIntersection = ApiIntersection (
lat = "40.72084",
lng = "-74.000661",
distance = "0.03",
street1 = "Broadway",
street2 = "Grand St ",
street1Bearing = "32",
street2Bearing = "124",
placename = "New York",
adminName1 = "New York",
adminName2 = "New York",
adminCode1 = "NY",
postalcode = "10013",
countryCode = "US",
mtfcc1 = "S1400",
mtfcc2 = "S1400"
)
}
class IntersectionServiceTest
extends Specification
with BaseTestSupport {
"Intersection Service" should {
"parse intersection from JSON" >>
new IntersectionServiceMock {
override def reqJson[T](url: String)(implicit parser: Parser[T], client: OkHttpClient = new OkHttpClient()): Future[T] =
Future.successful[T](validIntersection.asInstanceOf[T])
Await.result(IntersectionService.getIntersection(rcLocReq), Duration.Inf) must beEqualTo(
IntersectionResponse(Some(Intersection(street1 = "Grand St", street2 = "Broadway")))
)
}
}
}
When I run it, I get a runtime error with the following stack trace:
You shouldn't mixin `org.specs2.matcher.MustExpectations` or `org.specs2.matcher.ShouldExpectations` with `Scope`, use `org.specs2.matcher.MustThrownExpectations` or `org.specs2.matcher.ShouldThrownExpectations` instead.
java.lang.RuntimeException: You shouldn't mixin `org.specs2.matcher.MustExpectations` or `org.specs2.matcher.ShouldExpectations` with `Scope`, use `org.specs2.matcher.MustThrownExpectations` or `org.specs2.matcher.ShouldThrownExpectations` instead.
at org.specs2.matcher.Scope$class.$init$(ThrownExpectations.scala:133)
at org.tlc.whereat.services.IntersectionServiceTest$$anonfun$1$$anonfun$apply$1$$anon$1.<init>(IntersectionServiceTest.scala:52)
at org.tlc.whereat.services.IntersectionServiceTest$$anonfun$1$$anonfun$apply$1.apply(IntersectionServiceTest.scala:52)
at org.tlc.whereat.services.IntersectionServiceTest$$anonfun$1$$anonfun$apply$1.apply(IntersectionServiceTest.scala:52)
at org.specs2.matcher.Scope$$anon$3$$anonfun$asResult$1.apply(ThrownExpectations.scala:139)
at org.specs2.matcher.Scope$$anon$3$$anonfun$asResult$1.apply(ThrownExpectations.scala:139)
at org.specs2.execute.ResultExecution$class.execute(ResultExecution.scala:25)
at org.specs2.execute.ResultExecution$.execute(ResultExecution.scala:120)
at org.specs2.execute.Result$$anon$10.asResult(Result.scala:230)
at org.specs2.execute.AsResult$.apply(AsResult.scala:25)
at org.specs2.matcher.Scope$$anon$3.asResult(ThrownExpectations.scala:139)
at org.specs2.execute.AsResult$.apply(AsResult.scala:25)
at org.specs2.main.CommandLineAsResult$$anon$1.asResult(CommandLineAsResult.scala:17)
at org.specs2.main.CommandLineAsResult$$anonfun$apply$1.apply(CommandLineAsResult.scala:21)
at org.specs2.main.CommandLineAsResult$$anonfun$apply$1.apply(CommandLineAsResult.scala:21)
at org.specs2.specification.dsl.mutable.ExampleDsl1$BlockExample$$anonfun$$greater$greater$1.apply(ExampleDsl.scala:39)
at org.specs2.specification.dsl.mutable.ExampleDsl1$BlockExample$$anonfun$$greater$greater$1.apply(ExampleDsl.scala:39)
at org.specs2.specification.core.Execution$$anonfun$withEnv$1$$anonfun$apply$3.apply(Execution.scala:120)
at org.specs2.execute.ResultExecution$class.execute(ResultExecution.scala:25)
at org.specs2.execute.ResultExecution$.execute(ResultExecution.scala:120)
at org.specs2.execute.Result$$anon$10.asResult(Result.scala:230)
at org.specs2.execute.AsResult$.apply(AsResult.scala:25)
at org.specs2.specification.core.Execution$$anonfun$withEnv$1.apply(Execution.scala:120)
at org.specs2.specification.core.Execution$$anonfun$withEnv$1.apply(Execution.scala:120)
at org.specs2.specification.core.Execution$$anonfun$execute$2$$anonfun$apply$2.apply(Execution.scala:70)
at org.specs2.specification.core.Execution$$anonfun$execute$2$$anonfun$apply$2.apply(Execution.scala:70)
at org.specs2.specification.core.Execution.setResult(Execution.scala:76)
at org.specs2.specification.core.Execution$$anonfun$execute$2.apply(Execution.scala:70)
at org.specs2.specification.core.Execution$$anonfun$execute$2.apply(Execution.scala:70)
at scala.Option.fold(Option.scala:158)
at org.specs2.specification.core.Execution.execute(Execution.scala:70)
at org.specs2.specification.process.DefaultExecutor$$anonfun$executeFragment$1$$anonfun$apply$6.apply(Executor.scala:132)
at org.specs2.specification.process.DefaultExecutor$$anonfun$executeFragment$1$$anonfun$apply$6.apply(Executor.scala:130)
at org.specs2.specification.core.Fragment.updateExecution(Fragment.scala:44)
at org.specs2.specification.process.DefaultExecutor$$anonfun$executeFragment$1.apply(Executor.scala:130)
at org.specs2.specification.process.DefaultExecutor$$anonfun$executeFragment$1.apply(Executor.scala:129)
at org.specs2.specification.process.DefaultExecutor$$anonfun$sequencedExecution$1.executedFragment$lzycompute$1(Executor.scala:104)
at org.specs2.specification.process.DefaultExecutor$$anonfun$sequencedExecution$1.org$specs2$specification$process$DefaultExecutor$class$$anonfun$$executedFragment$1(Executor.scala:104)
at org.specs2.specification.process.DefaultExecutor$$anonfun$sequencedExecution$1$$anonfun$4.apply(Executor.scala:110)
at org.specs2.specification.process.DefaultExecutor$$anonfun$sequencedExecution$1$$anonfun$4.apply(Executor.scala:110)
at scalaz.concurrent.Task$.Try(Task.scala:299)
at org.specs2.data.Processes$$anonfun$start$1.apply(Processes.scala:96)
at org.specs2.data.Processes$$anonfun$start$1.apply(Processes.scala:96)
at scalaz.concurrent.Future$$anonfun$apply$13$$anon$3.call(Future.scala:325)
at scalaz.concurrent.Future$$anonfun$apply$13$$anon$3.call(Future.scala:325)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
That error originates from this line in the Specs2 source. I can't for the life of me figure out why I'm getting it. My best guess is that it's because one of my support files (AppContextTestSupport) mixes in specs2.mock.Mockito (which extends `Expectations`) and Scope.
BUT if I clone the 47 Degrees repo that has a nearly identical support setup (including the same version of AppContextTestSupport), all tests pass without a hiccup. Perhaps there's some small difference I'm not seeing? Maybe it has something to do with dependency mismatches in my build?
This is my build file:
import android.Dependencies.aar
import android.Keys._
android.Plugin.androidBuild
platformTarget in Android := "android-21"
name := "whereat"
scalaVersion := "2.11.6"
run <<= run in Android
resolvers ++= Seq(
Resolver.sonatypeRepo("releases"),
Resolver.mavenLocal,
"47deg Public" at "http://clinker.47deg.com/nexus/content/groups/public",
"jcenter" at "http://jcenter.bintray.com",
"scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
)
scalacOptions in (Compile, compile) ++=
(dependencyClasspath in Compile).value.files.map("-P:wartremover:cp:" + _.toURI.toURL)
scalacOptions in (Compile, compile) ++= Seq(
"-P:wartremover:traverser:macroid.warts.CheckUi"
)
scalacOptions ++= Seq("-feature", "-deprecation", "-target:jvm-1.7")
scalacOptions in Test ++= Seq("-Yrangepos")
javacOptions ++= Seq("-source", "1.7", "-target", "1.7")
libraryDependencies ++= Seq(
aar("org.macroid" %% "macroid" % "2.0.0-M4"),
aar("org.macroid" %% "macroid-viewable" % "2.0.0-M4"),
aar("com.android.support" % "support-v4" % "21.0.3"),
aar("com.fortysevendeg" %% "macroid-extras" % "0.1-SNAPSHOT"),
"com.google.android.gms" % "play-services" % "6.5.87",
"io.taig" %% "communicator" % "2.0.1",
"com.typesafe.play" %% "play-json" % "2.3.4",
"org.specs2" % "specs2-core_2.11" % "3.6-scalaz-7.0.7" % "test",
"org.specs2" % "specs2-mock_2.11" % "3.6-scalaz-7.0.7" % "test",
"org.specs2" % "specs2-junit_2.11" % "3.6-scalaz-7.0.7" % "test",
// "org.specs2" %% "specs2-core" % "2.4.17" % "test",
// "org.specs2" % "specs2-mock_2.11" % "3.0-M2" % "test",
"com.google.android" % "android" % "4.1.1.4" % "test",
compilerPlugin("org.brianmckenna" %% "wartremover" % "0.10")
)
proguardScala in Android := true
proguardOptions in Android ++= Seq(
"-ignorewarnings",
"-keep class scala.Dynamic"
)
apkbuildExcludes in Android ++= Seq (
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/NOTICE",
"META-INF/NOTICE.txt"
)
The commented lines were abortive attempts to see if using the same versions of specs2 as the 47 Degrees project uses (see here, here, and here) would fix the problem. It didn't!
Just for good measure, here are all the support files involved in the spec in question:
* AppContextSupport:
package org.tlc.whereat.support
import com.fortysevendeg.macroid.extras.AppContextProvider
import macroid.AppContext
import org.specs2.mock.Mockito
import org.specs2.specification.Scope
trait AppContextTestSupport
extends Mockito
with AppContextProvider
with TestConfig
with Scope {
implicit val appContextProvider: AppContext = mock[AppContext]
appContextProvider.get returns mockContext
}
* AppContextProvider:
package com.fortysevendeg.macroid.extras
import macroid.AppContext
trait AppContextProvider {
implicit val appContextProvider : AppContext
}
* TestConfig:
package org.tlc.whereat.support
import android.content.Context
import org.specs2.mock.Mockito
trait TestConfig extends Mockito {
val mockContext = mock[Context]
}
I'm working in IntelliJ 14 Ultimate, which has provided a host of headaches with regards SBT integration, but at the moment, I'm running my tests directly with `sbt test` and still getting the same errors as running through IntelliJ. In sum: I've tried just about everything and am completely stumped.
Can anyone help point me in the right direction? Perhaps just start by explaining what that error message is trying to say and how I might go about following its suggestion to use `MustThrownMatchers`? Would I need to mix in a different trait somewhere? Use a different operator in the actual specs? Construct my specs and mocks differently? Or am I totally on the wrong track?
Any help welcome,
Austin
@aguestuser |
aguestuser