How to use JUnit 5's #BeforeAll in Scala - scala

Junit5 has introduced #BeforeAll to signal that the annotated method should be executed before all tests in the current test class. However it has requirement of method being static.
I have following structure in my Scala test suite:
class BaseTest extends MockitoSugar {
#BeforeAll
def codeNeedeForAllTests() = { <== this does not work as as it is not static
println("calling codeNeedeForAllTests")
// other code
}
}
and other test classes:
class IndexerTest extends BaseTest {
#Test
def add() = {
//test code
}
}
So I want that codeNeedeForAllTests get called before all tests, however the catch is #BeforeAll's requirement of method being static, for which I need to make codeNeedeForAllTests as object to have static method in Scala.
Now in Scala, a class can not extend an object and and object also can not extend object.
I also tried creating companion object of BaseTest, but that also did not work, any clean approach to do this?

#BeforeAll methods in JUnit Jupiter (a.k.a., JUnit 5) do not have to be static.
The link you provided is to JUnit 4.
This link is to JUnit Jupiter.
Excerpt:
Denotes that the annotated method should be executed before all #Test, #RepeatedTest, #ParameterizedTest, and #TestFactory methods in the current class; analogous to JUnit 4’s #BeforeClass. Such methods are inherited (unless they are hidden or overridden) and must be static (unless the "per-class" test instance lifecycle is used).
And that links to the documentation on Test Instance Lifecycle.
Thus, you should be able to annotate your test class with #TestInstance(Lifecycle.PER_CLASS) and declare your #BeforeAll method as non-static.

You can do this with FunCpec.
package net
import org.scalatest._
class Test extends FunSpec with BeforeAndAfterAll {
override def beforeAll: Unit = {
println("beforeAll")
}
describe("running a test") {
println("in describe")
it("should do something") {
println("in my test")
assert(true)
}
}
}
If u'll test it in REPL:
scala> test
[info] Compiling 1 Scala source to /Users/kmmere/Workspace/Work/scalatest_before/target/scala-2.11/test-classes...
in describe
beforeAll
in my test
[info] Test:
[info] running a test
[info] - should do something
[info] Run completed in 99 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[success] Total time: 1 s, completed Oct 19, 2015 4:32:56 PM
Don't forget to include dependency of scalatest in yor build.sbt file:
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test
class FunSpec - facilitates a “behavior-driven” style of development (BDD), in which tests are combined with text that specifies the behavior the tests verify.
Recommended Usage: For teams coming from Ruby's RSpec tool, FunSpec will feel familiar and comfortable; More generally, for any team that prefers BDD, FunSpec's nesting and gentle guide to structuring text (with describe and it) provide an excellent general-purpose choice for writing specification-style tests.

Related

How can I use the Mock annotation in Scala?

I'm using scalatest and Scala 2.x. The following compiles/works:
import org.mockito.Mockito._
import org.mockito._
import org.mockito.ArgumentMatchers._
class Test1 extends AnyFunSuite {
val action = Mockito.mock(classOf[Action])
when(action.process(anyInt())).thenReturn("x")
...
}
This doesn't:
import org.mockito.Mockito._
import org.mockito._
import org.mockito.ArgumentMatchers._
class Test1 extends AnyFunSuite {
#Mock
val action = Action()
when(action.process(anyInt())).thenReturn("x")
...
}
I get this exception on the "when" line:
[info] Test1 *** ABORTED ***
[info] org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced or misused argument matcher detected here:
[info]
[info] -> at Test1.<init>(Test1.scala:17)
[info]
[info] You cannot use argument matchers outside of verification or stubbing.
[info] Examples of correct usage of argument matchers:
[info] when(mock.get(anyInt())).thenReturn(null);
[info] doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject());
[info] verify(mock).someMethod(contains("foo"))
[info]
[info] This message may appear after an NullPointerException if the last matcher is returning an object
[info] like any() but the stubbed method signature expect a primitive argument, in this case,
[info] use primitive alternatives.
[info] when(mock.get(any())); // bad use, will raise NPE
[info] when(mock.get(anyInt())); // correct usage use
[info]
[info] Also, this error might show up because you use argument matchers with methods that cannot be mocked.
[info] Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
[info] Mocking methods declared on non-public parent classes is not supported.
[info] at Test1.<init>(Test1.scala:17)
[info] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[info] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
[info] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
[info] at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
[info] at java.lang.Class.newInstance(Class.java:442)
[info] at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:450)
[info] at sbt.TestRunner.runTest$1(TestFramework.scala:140)
[info] at sbt.TestRunner.run(TestFramework.scala:155)
[info] at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:318)
[info] ...
Personally, I would avoid mocking altogether, but if you actually need it, in Scala I would use something like ScalaMock which cooperates better with the language.
class Test1 extends AnyFunSuite {
val action = mock[Action]
(action.process _).expects(*).returning("x")
...
}
From what I see #Mock annotation relies on some custom JUnit runner which injects logic into test class by combining Guice/Spring-like dependency injection with mocking. But you are using it wrong:
// Here you would have to configure JUnit runner
// (but Scala test libraries are not really JUnit-based)
class Test1 extends AnyFunSuite {
// Here you would have to configure action as var
// and not initialize it yourself, so that runner could
// create the mocks and put them here using reflection
// after instance initialization
#Mock
var action: Action = _
// Here you could not have the test logic be implemented
// as vals or vars, because it would be executed in object's
// constructor - when action is still unititialized
// (therefore null). JUnit doesn't show this issue as it
// uses #Mock on class attributes, and put #Test logic in
// methods - called only after constructor is completed
// and JUnitRunner injects additional logic
// ...
}
You could totally do it like:
// use JUnit instead of Scalatest
#RunWith(classOf[MockitoJUnitRunner])
class Test1 {
#Mock
var action: Action = _
#Test
def example(): Unit = {
// ...
}
}
so it is possible to make it work in Scala, just not with the non-JUnit-based testing libraries. If you are interested in Mockito conventions in ScalaTest see a dedicated documentation page.

Play 2.5 Guice errors when compiling

I am currently in the process of migrating my app from Play 2.4 to Play 2.5 So far, it's been a massive pain.
Right now, I am trying to make my tests to pass.
I have one controller with some injected params
class UserLogin #Inject() (
loginService: UserLoginService,
authConfig: AuthConfig,
oAuthConfig: OAuthConfig,
env: PureEnvironment,
//stringResources: StringResources,
messagesApi: MessagesApi
) extends BaseController(messagesApi: MessagesApi) {
//endpoints
}
From the testing side, I have them defined to be injected with Guice
object IdentityManagerModuleMock extends AbstractModule with ScalaModule {
def configure: Unit = {
bind[PureEnvironment].toInstance(MockEnvironment)
val conf = Configuration.reference
val messagesApi = new DefaultMessagesApi(Environment.simple(), conf, new DefaultLangs(conf))
bind[MessagesApi].toInstance(messagesApi)
bind[AuthConfig].toInstance(
AuthConfig(
"https://localhost",
30,
3600,
86400,
Seq(InetAddress.getByName("127.0.0.1")),
Seq(InetAddress.getByName("127.0.0.1")),
"https://localhost/login"
)
)
bind[OAuthConfig].toInstance(oAuthConfigMock)
bind[UserLoginService].to[UserLoginServiceImpl]
}
val injector = Guice.createInjector(IdentityManagerModuleMock)
When I enable the routes in my routes file,
POST /login com.dummy.im.controllers.UserLogin.login
POST /reset com.dummy.im.controllers.UserLogin.reset
1) No implementation for com.dummy.im.components.UserLoginService was bound.
[info] while locating com.dummy.im.components.UserLoginService
[info] for parameter 0 at com.dummy.im.controllers.UserLogin.<init>(UserLogin.scala:24)
2) No implementation for com.dummy.platform.PureEnvironment was bound.
[info] while locating com.dummy.platform.PureEnvironment
[info] for parameter 3 at com.dummy.im.controllers.UserLogin.<init>(UserLogin.scala:24)
3) No implementation for scala.collection.Seq<java.net.InetAddress> was bound.
[info] while locating scala.collection.Seq<java.net.InetAddress>
[info] for parameter 4 at com.dummy.im.config.AuthConfig.<init>(AuthConfig.scala:13)
5) Could not find a suitable constructor in java.lang.Integer. Classes must have either one
(and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
[info] at java.lang.Integer.class(Integer.java:52)
[info] while locating java.lang.Integer
[info] for parameter 1 at com.dummy.im.config.AuthConfig.<init>(AuthConfig.scala:13)
The weird thing is that I have a very similar config for actual running the app, without the MessagesAPI (injected by Play, as far as I know) and those config objects which are read from the .conf file.
object IdentityManagerModule extends AbstractModule with ScalaModule {
def configure: Unit = {
bind[PureEnvironment].to[PlayProductionEnvironmentImpl]
bind[OauthRepository].to[OauthRepositoryImpl]
bind[UserLoginService].to[UserLoginServiceImpl]
}
}
And it runs just fine.
The main thing that I changed was to add the dependency to MessagesAPI in the controller.
I don't understand why Guice fails to see the things that are binded in IdentityManagerModuleMock.
Any ideas are more than welcomed. I have read and tried all I could think of for the past several days.
EDIT:
We have a custom app loader
class CustomApplicationLoader extends GuiceApplicationLoader() {
//config validation
override def builder(context: ApplicationLoader.Context): GuiceApplicationBuilder = {
val conf = context.initialConfiguration
initialBuilder
.in(context.environment)
.loadConfig(conf)
.overrides(overrides(context): _*)
.bindings(
MiscModule,
CommonConfigurationModule,
IdentityManagerConfigModule,
IdentityManagerModule,
ApplicationLifecycleModule
)
}
}
In the .conf file, it's used as
play.application.loader = "com.dummy.guice.CustomApplicationLoader"
It doesn't look like you need a custom application loader for what you're doing. If you disable the out of the box MessagesApi and then add your own modules through application.conf, that should mean you don't have to override MessagesApi.
https://www.playframework.com/documentation/2.5.x/ScalaPlayModules#Registration-Overrides
If you're running tests involving Guice, you can use WithApplication.
https://www.playframework.com/documentation/2.5.x/ScalaFunctionalTestingWithSpecs2#WithApplication
You shouldn't ever have to call Guice.createInjector directly, because WithApplication will call out to GuiceApplicationBuilder:
https://www.playframework.com/documentation/2.5.x/ScalaTestingWithGuice#GuiceApplicationBuilder
Check out the example projects: they all have "2.5.x" branches and most have tests integrated into them, and leverage them as a guide:
https://github.com/playframework?utf8=%E2%9C%93&q=examples&type=&language=

ScalaMock, Unexpected call: <mock-1> when sharing mock instance between tests

I am using Scala 2.10 with ScalaMock 3.6.
I have a quite simple test case with 4 test scenarios. I have created a mock object for those tests to use (imitating file system):
class ProcessingOperatorTest extends FlatSpec with Matchers with BeforeAndAfterEach with MockFactory {
...
val fakeFS = mock[FileIO]
(fakeFS.createFile _).expects(*).returns(true).anyNumberOfTimes()
(fakeFS.exist _).expects(where { (p: String) => p.contains(existing) }).returns(true).anyNumberOfTimes()
(fakeFS.exist _).expects(where { (p: String) => p.contains(notExisting) }).returns(false).anyNumberOfTimes()
behavior of "Something"
it should "test 1" in {
...
}
it should "test 2" in {
...
}
it should "test 3" in {
...
}
it should "test 4" in {
...
}
Now:
1st test does not use any of mocked methods (but needs the mock object)
2nd test uses only existing mock method
3rd test uses both existing and not existing mock methods
4th test uses all methods, (also createFile)
Now, for some reason, when running all those tests together, 4th test fails giving me the following error. If running separately, it will pass.
Unexpected call: <mock-1> FileIO.exist(notExisting)
Expected:
inAnyOrder {
}
Actual:
<mock-1> FileIO.exist(notExisting)
ScalaTestFailureLocation: scala.Option at (Option.scala:120)
org.scalatest.exceptions.TestFailedException: Unexpected call: <mock-1> FileIO.exist(notExisting)
...
The other walkaround is to copy-paste the mock declaration and its behaviour inside 4th it should { ... } test scenario. Tests work then (separately, and all together).
Why is global mock instance failing?
I can try to prepare a similar test scenario as separate sbt project if needed.
Mix in org.scalatest.OneInstancePerTest as described here:
class ProcessingOperatorTest extends FlatSpec
with Matchers
with BeforeAndAfterEach
with MockFactory
with OneInstancePerTest {
...
}

Setting to get SBT to fail fast (stop) on an error

I am running a multi-project SBT (v0.13) build and would like it to fail fast (stop) on the first error (compile) encountered in a sub-project.
The current behavior is that when something fails to compile in a sub-project, the build continues (to compile all the other sub-projects).
Is there a way/setting to make SBT stop and exit as soon as the first sub-project with a compile error is encountered?
In short, to my knowledge, no, SBT can not "fail fast" on a compiler or test error.
To my knowledge SBT doesn't control this. SBT is just going to invoke the appropriate test framework when it inspects your unit tests. The test framework can then decide what order to run the tests in, to run them concurrently, how to report issues, etc. Of course each test framework has its own features, configuration, and conventions. The two most widely used test frameworks for Scala are ScalaTest and Specs2.
Fortunately you can get the behavior you've requested in either Specs2 or ScalaTest. I've provided simple unit test examples below that fail early.
ScalaTest
You can get fail-fast behavior in ScalaTest for single test Suites by mixing in the CancelAfterFailure trait. For example, this test would execute the first test, fail the second, and show the third as cancelled.
class SomeSuite extends FunSuite with CancelAfterFailure with Assertions {
test("first") {
println("first")
}
test("second") {
assert(false)
}
test("third") {
println("third")
}
}
Specs2
Similar to ScalaTest you can control behavior in Specs2 on a per-Specification basis. To get fail-fast-like behavior you need to add two Arguments to your Specification: sequential and stopOnFail. See the docs for a full list of arguments you can set. (You do need both if you want an obvious linear ordering since Specs2 by default will execute your tests concurrently!)
class SomeSpec extends Specification {
sequential
stopOnFail
"specs2" should {
"first" in {
println("first")
ok
}
"second" in {
"foo" must equalTo ("bar")
}
"third" in {
println("third")
}
}
}
I've discovered that throwing a new java.lang.VirtualMachineError() {} halts the task immediately, while all other kinds of exceptions I have tried so far were swallowed.
I've tried ThreadDeath, InterruptedException, LinkageError, and ControlThrowable, and of course, the usual RuntimeException and such (the list came from scala.util.control.NonFatal)
<project root>/project/BuildUtils.scala
import sbt._
import sbt.Def._
object BuildUtils {
private def details(inc: Incomplete): Seq[Throwable] =
inc.directCause.toSeq ++ inc.causes.flatMap(details)
implicit class TaskSyntax[A](private val task: Initialize[Task[A]]) extends AnyVal {
def haltWhenFailed: Initialize[Task[A]] = task.result.map {
case Inc(cause) =>
throw new VirtualMachineError({
s"""Task has failed during execution.
|TaskNode: ${cause.node}
|DirectCause: ${details(cause).map(_.getMessage).distinct}
|""".stripMargin
}) {}
case Value(value) => value
}
}
}
<project root>/build.sbt
import BuildUtils._
lazy val ciBuild = taskKey[Unit]("Compile and run test")
ciBuild := {
val t1 # _ = (Test / compile).haltWhenFailed.value
val t2 # _ = (Test / test).haltWhenFailed.value
}
ciBuild will halt at first compilation error with rather verbose stacktrace and the specified message.

Excluding a ScalaTest test when calling my tests from within sbt

I want to write a test that calls a remote server and validates the response because the server may change (it's not under my control). To do this I figure I'd give it a tag (RemoteTest) and then exclude it when calling the runner:
sbt> test-only * -- -l RemoteTest
However, when doing this all my tests are run, including RemoteTest. How do I call the runner from within sbt so that it is excluded?
If you have the following:-
package com.test
import org.scalatest.FlatSpec
import org.scalatest.Tag
object SlowTest extends Tag("com.mycompany.tags.SlowTest")
object DbTest extends Tag("com.mycompany.tags.DbTest")
class TestSuite extends FlatSpec {
"The Scala language" must "add correctly" taggedAs(SlowTest) in {
val sum = 1 + 1
assert(sum === 2)
}
it must "subtract correctly" taggedAs(SlowTest, DbTest) in {
val diff = 4 - 1
assert(diff === 3)
}
}
To exclude DbTest tag, you would do:-
test-only * -- -l com.mycompany.tags.DbTest
Note that you'll need to include the full tag name. If it's still not working for you, would you mind sharing part of source code that's not working?