I want to add integration tests to my project using cucumber feature files. I have got this working using this project as an example: https://github.com/jecklgamis/cucumber-jvm-scala-example
The problem I am running into is when I want to mock some objects. ScalaMock and EasyMock all seem to need scalatest or something similar.
My build.sbt has these lines:
libraryDependencies ++= Seq(
"io.cucumber" %% "cucumber-scala" % "2.0.1" % Test,
"io.cucumber" % "cucumber-junit" % "2.0.1" % Test,
"org.scalamock" %% "scalamock" % "4.0.0" % Test,
"org.scalatest" %% "scalatest" % "3.0.1" % Test,
etc..
My stepdef file has this:
import com.typesafe.config.{Config, ConfigFactory}
import cucumber.api.scala.{EN, ScalaDsl}
import eu.xeli.jpigpio.JPigpio
class StepDefs extends ScalaDsl with EN {
var config: Config = null
var jpigpio: JPigpio = null
Given("""^an instance of pigpio$""") { () =>
jpigpio = mock[JPigpio]
}
}
The mock[JPigpio] call gives a symbol not found error. I assume because this class does not extend MockFactory.
How can I use scalamock outside of an MockFactory class?
Bit of a quick and dirty example that does not pull in Scalatest, but I'm sure you can piece the rest together. I'd actually be curious to see this working with Cucumber :)
import org.scalamock.MockFactoryBase
import org.scalamock.clazz.Mock
object NoScalaTestExample extends Mock {
trait Cat {
def meow(): Unit
def isHungry: Boolean
}
class MyMockFactoryBase extends MockFactoryBase {
override type ExpectationException = Exception
override protected def newExpectationException(message: String, methodName: Option[Symbol]): Exception =
throw new Exception(s"$message, $methodName")
def verifyAll(): Unit = withExpectations(() => ())
}
implicit var mc: MyMockFactoryBase = _
var cat: Cat = _
def main(args: Array[String]): Unit = {
// given: I have a mock context
mc = new MyMockFactoryBase
// and am mocking a cat
cat = mc.mock[Cat]
// and the cat meows
cat.meow _ expects() once()
// and the cat is always hungry
cat.isHungry _ expects() returning true anyNumberOfTimes()
// then the cat needs feeding
assert(cat.isHungry)
// and the mock verifies
mc.verifyAll()
}
}
This will actually throw as the meows expectation is not satisfied (just to demo)
Exception in thread "main" java.lang.Exception: Unsatisfied expectation:
Expected:
inAnyOrder {
<mock-1> Cat.meow() once (never called - UNSATISFIED)
<mock-1> Cat.isHungry() any number of times (called once)
}
Actual:
<mock-1> Cat.isHungry(), None
at NoScalaTestExample$MyMockFactoryBase.newExpectationException(NoScalaTestExample.scala:13)
at NoScalaTestExample$MyMockFactoryBase.newExpectationException(NoScalaTestExample.scala:10)
at org.scalamock.context.MockContext$class.reportUnsatisfiedExpectation(MockContext.scala:45)
at NoScalaTestExample$MyMockFactoryBase.reportUnsatisfiedExpectation(NoScalaTestExample.scala:10)
Related
I am having a class which extends LazyLogging trait
class TaskProcessor()
extends Processor
with LazyLogging {
def a1() = {
logger.info("Test logging")
}
}
Now, I want to test whether my logging works. So I followed this example Unit test logger messages using specs2 + scalalogging and wrote my test as follows
"TaskProcessor" should "test logging" in {
val mockLogger = mock[Logger]
val testable = new TaskProcessor {
override val logger: Logger = mockLogger
}
verify(mockLogger).info("Test logging")
}
I get the following error
Error:(32, 20) overriding lazy value logger in trait LazyLogging of type com.typesafe.scalalogging.Logger;
value logger must be declared lazy to override a concrete lazy value
override val logger: Logger = mockLogger
To resolve this, I modify statement
override val logger: Logger = mockLogger
to
override lazy val logger: Logger = mockLogger
I get the following error
Cannot mock/spy class com.typesafe.scalalogging.Logger
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class com.typesafe.scalalogging.Logger
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at org.scalatest.mockito.MockitoSugar.mock(MockitoSugar.scala:73)
at org.scalatest.mockito.MockitoSugar.mock$(MockitoSugar.scala:72)
My dependecies are as follows
"org.scalatest" %% "scalatest" % "3.0.5" % "test",
"org.mockito" % "mockito-all" % "1.10.19" % Test,
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.2",
Can anyone please guide me as to how I can mock the logger and do the testing.
The problem is that com.typesafe.scalalogging.Logger class cannot be mocked because it's final, but we still can mock underlying org.slf4j.Logger.
import org.scalatest.mockito.MockitoSugar
import org.slf4j.{Logger => UnderlyingLogger}
import com.typesafe.scalalogging.Logger
import org.scalatest.{Matchers, WordSpec, FlatSpec}
import org.mockito.Mockito._
class TaskProcessorSpec extends FlatSpec with Matchers with MockitoSugar {
"TaskProcessor" should "test logging" in {
val mockLogger = mock[UnderlyingLogger]
when(mockLogger.isInfoEnabled).thenReturn(true)
val testable = new TaskProcessor {
override lazy val logger = Logger(mockLogger)
}
testable.a1()
verify(mockLogger).info("Test logging")
}
}
I am using Google Guice as DI framework and I am writing Unit Tests for my classes which use Google Guice. I am also trying to do partial mocking.
Here is the code I wrote
class Test1 {
def test1() = "I do test1"
}
class Test2 {
def test2() = "I do test2"
}
class TestPartialMock #Inject()(t1: Test1, t2: Test2) {
def test3() = "I do test3"
def createList() : List[String] = List(t1.test1(), t2.test2(), test3())
}
My objective is to write a test case for the code above, but I ONLY want to mock test3
I wrote this test case
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
bind[Test1]
bind[Test2]
val x = mock[TestPartialMock]
when(x.test3()).thenReturn("I am mocked")
when(x.createList()).thenCallRealMethod()
bind(classOf[TestPartialMock]).toInstance(x)
}
}
class PartialMockTest extends FunSpec with Matchers {
describe("we are testing workhorse but mock test3") {
it("should return mock for test3") {
val module = new TestModule
val injector = Guice.createInjector(module)
val tpm = injector.getInstance(classOf[TestPartialMock])
val result = tpm.workHorse()
result should contain ("I do test2")
result should contain ("I do test1")
result should contain ("I am mocked")
result should not contain ("I do test3")
}
}
}
However the test fails with a null pointer exception on the dependencies (call to t1)
java.lang.NullPointerException
at TestPartialMock.createList(TestPartialMock.scala:9)
at PartialMockTest.$anonfun$new$2(PartialMockTest.scala:16)
at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
at org.scalatest.Transformer.apply(Transformer.scala:22)
at org.scalatest.Transformer.apply(Transformer.scala:20)
at org.scalatest.FunSpecLike$$anon$1.apply(FunSpecLike.scala:454)
So how can I have the injected dependencies along with the mocking for test3 method?
Here are my dependencies if you need to look at those
"net.codingwell" %% "scala-guice" % "4.1.0",
"org.scalatest" % "scalatest_2.12" % "3.0.3",
"org.scalamock" % "scalamock-scalatest-support_2.12" % "3.5.0",
"org.mockito" % "mockito-core" % "2.7.22"
The trick is that Mockito doesn't call the constructor of the (base) class when a mocked object is created. Thus the dependencies of the TestPartialMock are not initialized. The simplest way to work this around is to spy on a real object that you can create with whatever configuration you want
class TestModule extends AbstractModule with ScalaModule with MockitoSugar {
override def configure() = {
//bind[Test1]
//bind[Test2]
//val x = mock[TestPartialMock]
val realObject = new TestPartialMock(new Test1, new Test2)
val x = spy(realObject)
when(x.test3()).thenReturn("I am mocked")
when(x.createList()).thenCallRealMethod()
bind(classOf[TestPartialMock]).toInstance(x)
}
}
Why can I not tell a mock that it should expect an instance of a class without explicitly giving the type? Here is what I mean:
val myClass = new MyClass(...)
val traitMock = mock[MyTrait]
(traitMock.mymethod _).expects(myClass).returning(12.3)
does not work, while
val myClass: MyClass = new MyClass(...)
val traitMock = mock[MyTrait]
(traitMock.mymethod _).expects(myClass).returning(12.3)
does work. How come the type can not be inferred?
My testing part in build.sbt is
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.0.0" % "test"
exclude("org.scala-lang", "scala-reflect")
exclude("org.scala-lang.modules", "scala-xml")
)
libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "3.3.0" % "test"
Since I was asked for MyClass (it is SpacePoint here):
trait SpacePoint {
val location: SpaceLocation
}
val sp = new SpacePoint {
override val location: SpaceLocation = new SpaceLocation(DenseVector(1.0, 1.0))
}
So actually it works for me. Let me mention that type inference in the code:
val myClass = new MyClass(...)
has nothing to do with ScalaMock but is guaranteed by scala itself. Below I will specify working sample with library versions and sources of the classes.
Testing libraries:
"org.scalatest" %% "scalatest" % "2.2.4" % "test",
"org.scalamock" %% "scalamock-scalatest-support" % "3.2.2" % "test"
Source code of classes:
class MyClass(val something: String)
trait MyTrait {
def mymethod(smth: MyClass): Double
}
Source code of test:
import org.scalamock.scalatest.MockFactory
import org.scalatest.{Matchers, WordSpec}
class ScalamockTest extends WordSpec with MockFactory with Matchers {
"ScalaMock" should {
"infers type" in {
val myClass = new MyClass("Hello")
val traitMock = mock[MyTrait]
(traitMock.mymethod _).expects(myClass).returning(12.3)
traitMock.mymethod(myClass) shouldBe 12.3
}
}
}
Hope it helps. Will be ready to update answer once you provide more details.
I'm struggling to get the following test working, basically I want the mocked service call to throw an exception when first called, and work ok on the 2nd invocation. The service method returns nothing (void/Unit), written in scala
import org.mockito.{Mockito}
import org.scalatest.mock.MockitoSugar
import org.scalatest.{BeforeAndAfter, FeatureSpec}
import org.mockito.Mockito._
class MyMocksSpec extends FeatureSpec with BeforeAndAfter with MockitoSugar {
var myService: MyService = _
var myController: MyController = _
before {
myService = mock[MyService]
myController = new MyController(myService)
}
feature("a feature") {
scenario("a scenario") {
Mockito.doThrow(new RuntimeException).when(myService.sideEffect())
Mockito.doNothing().when(myService.sideEffect())
myController.showWebPage()
myController.showWebPage()
verify(myService, atLeastOnce()).sayHello("tony")
verify(myService, atLeastOnce()).sideEffect()
}
}
}
class MyService {
def sayHello(name: String) = {
"hello " + name
}
def sideEffect(): Unit = {
println("well i'm not doing much")
}
}
class MyController(myService: MyService) {
def showWebPage(): Unit = {
myService.sideEffect()
myService.sayHello("tony")
}
}
Here is the build.sbt file
name := """camel-scala"""
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies ++= {
val scalaTestVersion = "2.2.4"
Seq(
"org.scalatest" %% "scalatest" % scalaTestVersion % "test",
"org.mockito" % "mockito-all" % "1.10.19")
}
Unfinished stubbing detected here:
-> at MyMocksSpec$$anonfun$2$$anonfun$apply$mcV$sp$1.apply$mcV$sp(MyMocksSpec.scala:24)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
I followed the example (I think) here
Turns out the I was setting up the mock incorrectly, solution below..
Mockito.doThrow(new RuntimeException).when(myService).sideEffect()
Mockito.doNothing().when(myService).sideEffect()
instead of the incorrect
Mockito.doThrow(new RuntimeException).when(myService.sideEffect())
Mockito.doNothing().when(myService.sideEffect())
I am trying to use spray route and want to test it with Spray-TestKit.
I am using :
- Scala 2.10.3
- Akka 2.3.3
- Spray 1.3.1
I create a trait extending HttpService, where I define a route :
trait MyService extends HttpService with CoreAccess {
import greentee.am.endpoint.tsmsp.tsmSPJsonSupport._
val myRoute = {
path("resources"/"ping") {
get {
complete(OK, "pong")
}
}
}
}
I deleted part of the route which was not relevant.
CoreAccess is a trait extending Actor, because I have methods in that trait access the ActorSystem. (I don't know who to retrieve ActorSelection from a trait without it extending an actor)
Then I create a test Specification
import MyService
import org.specs2.mutable.Specification
import spray.testkit.Specs2RouteTest
import spray.http.StatusCodes._
class RegistrationRouteSpecification extends Specification with Specs2RouteTest with MyService {
def actorRefFactory = system
"The EndPoint " should {
"return Pong to a Get request to the ping" in {
Get("/resources/ping") ~> myRoute ~> check {
status === OK
responseAs[String] === "pong"
}
}
}
}
When I try to execute the test, I get the following compilation error:
[info] Compiling 1 Scala source to /Users/IdeaProjects/endpoint/target/scala-2.10/test-classes...
[error] /Users/IdeaProjects/endpoint/src/test/scala/RegistrationRouteSpecification.scala:19: could not find implicit value for parameter ta: RegistrationRouteSpecification.this.TildeArrow[spray.routing.RequestContext,Unit]
[error] Get("/resources/ping") ~> myRoute ~> check {
[error] ^
[error] one error found
I answer my own question.
I corrected my Build.scala to use the following lines:
val scalaCheck = "org.scalacheck" %% "scalacheck" % Versions.scalaCheckVersion % "test"
val scalaTest = "org.scalatest" %% "scalatest" % "2.2.0" % "test"
Instead of using a simple '%' and supplying a dedicated version.