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.
Related
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.
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=
Scala version 2.11.8
I have parent class
abstract class FR(externalId:String, code:String, message:String) extends Serializable {
val this.externalId=externalId;
val this.code = code;
val this.message = message;
def toString:String={
return "FRworks";
}
}
The child class is:
class RD extends FR {
def this(lpTransaction:LPTransaction)={
externalId =lpTransaction.getField("somethinghere").toString
...
}
}
The error is:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\RD.scala:9: 'this' expected but identifier found.
[error] externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
when I add this in front of externalId the error still:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:9: '}' expected but '.' found.
[error] this.externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:12: eof expected but '}' found.
[error] }
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
Your code is very Java-influenced. There are couple of things wrong here; when you fix the obvious ones like missing class parameters in RD it boils down to not being able to reassign to val.
Let me give you an improved, Scala-fied version of the whole code.
abstract class FR(val externalId: String, val code: String, val message: String) extends Serializable
// dummy class
class LPTransaction {
def getField(s: String) = s
}
class RD(externalId: String, code: String, message: String) extends FR(externalId, code, message) {
def this(lpTransaction: LPTransaction) = {
this(lpTransaction.getField("somethinghere").toString, "defaultCode", "defaultMessage")
}
println(externalId)
}
val a = new RD(new LPTransaction) // prints "somethinghere"
Main improvements are:
You don't need private fields to be populated using arguments in the constructor. Scala favors immutability. Make your class arguments "vals", this means they will be available as public fields (instead of getters you will access them directly; this is contrary to OOP's encapsulation principle, but here it's ok because nobody can mess with them anyway since they are immutable; they may only be fetched)
Your subclass RD should be taking same fields as parameters as its parent class. Of course, you can then define an auxiliary constructor that takes only LPTransaction, but then you need to feed the parent class with some default values for the other parameters.
The rest kind of follows from this. I added the dummy implementation of LPTransaction to be able to compile. I also threw in a println statement in RD class just for the sake of example. Feel free to ask if something's not clear.
//scala automatically generates getters for passed in params. No need to set them explicitly. For immutable params use val, for mutable use var
abstract class FR(var externalId:String, val code:String, val message:String) extends Serializable {
//need to use override annotation for superclass methods
override def toString:String={
return "FRworks";
}
}
// notice how constructor parameters are passed to the base class when defining the child class.
class RD extends FR("someID","code","msg") {
def printId() = println(externalId)
}
val x = new RD
x.externalId = "new ID" //works because externalId is var (mutable)
x.code = "new code" //error because code is val (immutable)
x.printId //correctly prints the external id: someID
I found very strange issue with Scala import.
I wrote sample class:
package test_scala_predef
object App extends App {
classOf[T]
println( "Hello World!" )
}
class T {
}
This class compiles without any errors.
But, if I add
import scala.Predef.String
then I get compilation errors:
[INFO] Compiling 1 source files to /home/uthark/src/_/test_scala_predef/target/classes at 1374028063588
[ERROR] /home/uthark/src/_/test_scala_predef/src/main/scala/test_scala_predef/App.scala:10: error: not found: value classOf
[INFO] classOf[T]
[INFO] ^
[ERROR] /home/uthark/src/_/test_scala_predef/src/main/scala/test_scala_predef/App.scala:11: error: not found: value println
[INFO] println( "Hello World!" )
[INFO] ^
[ERROR] two errors found
I have an idea, that after I add specific import from scala.Predef, then implict import of scala.Predef._ is not added. But I can't find anything about it in the Scala documentation. Can anyone point me to the relevant section in documentation?
I checked Scala Language Specification (PDF), section 12.5 covering scala.Predef but also didn't find any related.
I use latest stable scala version available (2.10.2 at the moment)
I found the answer in the sources.
https://github.com/scala/scala/blob/master/src/compiler/scala/tools/nsc/typechecker/Contexts.scala:
/** List of symbols to import from in a root context. Typically that
* is `java.lang`, `scala`, and [[scala.Predef]], in that order. Exceptions:
*
* - if option `-Yno-imports` is given, nothing is imported
* - if the unit is java defined, only `java.lang` is imported
* - if option `-Yno-predef` is given, if the unit body has an import of Predef
* among its leading imports, or if the tree is [[scala.Predef]], `Predef` is not imported.
*/
protected def rootImports(unit: CompilationUnit): List[Symbol] = {
assert(definitions.isDefinitionsInitialized, "definitions uninitialized")
if (settings.noimports) Nil
else if (unit.isJava) RootImports.javaList
else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) {
debuglog("Omitted import of Predef._ for " + unit)
RootImports.javaAndScalaList
}
else RootImports.completeList
}
This answers my question.
The key sentence is this:
Exceptions: ... if the unit body has an import of Predef among its leading imports
Also, in chat on #scala irc it was suggested to create bug in Scala issue tracking system, so I did it. https://issues.scala-lang.org/browse/SI-7672
I have a method defined in Java like:
void foo(int x, Thing... things)
I need to override that in Scala, but both of these give errors:
override def foo(x: Int, things: Thing*)
override def foo(x: Int, things: Array[Thing])
The errors refer to <repeated...> but I don't know what that is.
Update
Ugg... nevermind. I'm in 2.10.0, and I had mis-typed something and didn't have a method body. Then I got confused by this error message, which still seems a odd to me. In SBT:
> compile
[info] Compiling 1 Scala source to [...]/target/scala-2.10/classes...
[error] [...]/src/main/scala/Translator.scala:41: class MyMethodVisitor needs to be abstract, since method visitTableSwitchInsn is not defined
[error] (Note that org.objectweb.asm.Label* does not match <repeated...>[org.objectweb.asm.Label])
[error] class MyMethodVisitor extends MethodVisitor (Opcodes.ASM4) {
[error] ^
The problem is that my visitTableSwitchInsn simply lacks a body, but the error suggests that the problem is the type of the varargs parameter.
Java:
package rrs.scribble;
public
class VA1
{
public int va1(int... ints) {
return ints.length;
}
}
Scala:
package rrs.scribble
class VA1S
extends VA1
{
override
def va1(ints: Int*): Int =
ints.length * 2
}
SBT:
> ~compile
[info] Compiling 1 Scala source and 1 Java source to …/scribble/target/scala-2.10/classes...
[success] Total time: 4 s, completed Jan 15, 2013 3:48:14 PM
1. Waiting for source changes... (press enter to interrupt)
This is Scala 2.10, which is consistent with #TravisBrown's comment.