Search code examples
unit-testingmockitospecs2scalatra

How to reset a mock invocation counter using Scalatra, Specs2, Mockito


I never expected that I will need to ask a question on this site because everything is already answered normally but with Scalatra... I haven't find a lot of information so here it is:

I'm not experienced with all that so maybe I'm missing something but from what I understand, if I want to test the API that I develop on Scalatra, I need to start the server everytime I run the test suit, right ?

Second question, how can I reset the invocation counter on a method so I don't have to calculate how many times the method has been called since the beginning of the test suite ? Right now using this give me more than one because it counts the previous test.

there was one(*instance*).*method*(*parameter*)

I can still get around the problem by either counting or putting the test as first test for now but it's not a sustainable solution...

Other thing that I found: Reset method on the mock... not found http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#17

Isolating the test in a class scope: We need to add

val servlet = new Servlet(eventRepoMock)
addServlet(servlet, "/*")

and we can't repeat the addServlet at every initialization https://etorreborre.github.io/specs2/guide/SPECS2-3.5/org.specs2.guide.Isolation.html

Last thing that I try is:

servlet.repo = mock[EventRepo]

but repo being a value, I can't change it like this.

Neither of these "solutions" feel very clean so I was wondering if someone had a genius idea that can solve that mess !?

Thank you in advance !

EDIT: Thanks to Eric's comment the above question are solve(that was easy) but now I have problem because I'm testing the get/post which are asynchronous call so resetting the mock does not happen at the right time... Any suggestion ?

Here's a simplified version of the code:

class EventServiceSpec extends ScalatraSpec with Mockito with Before { def is = s2"""
Event Service

GET an existing event
must return status 200                              $get_status200
must return the event with id = :id                 $get_rightEventElement
must call findById once on the event repository     $get_findByIdOnRepo
"""

lazy val anEvent = Event(1, "Some Event"
lazy val eventsBaseUrl = "/events"
lazy val existingEventUrl = s"$eventsBaseUrl/${anEvent.id}"

lazy val eventRepoMock = mock[EventRepository]

lazy val servlet = new Servlet(eventRepoMock)
addServlet(servlet, "/*")

def before = {
    eventRepoMock.findById(anEvent.id) returns Option(anEvent)
    eventRepoMock.findById(unexistingId) returns None
    eventRepoMock.save(anEvent) returns Option(anEvent)
}

def get_status200 = get(existingEventUrl){
    status must_== 200
}

def get_findByIdOnRepo = get(existingEventUrl){
    // TODO count considering previous test... need to find a cleaner way
    there was three(eventRepoMock).findById(anEvent.id)
}

Solution

  • All org.mockito.Mockito functions can still be used in a specs2 specification and reset is one of them.

    Now, since you are sharing the state of a mock object across several examples, you not only need to reset the mock state before each example but you also need to make your specification sequential:

    class EventServiceSpec extends ScalatraSpec with Mockito 
      with BeforeAll with BeforeEach { 
      def is = sequential ^ s2"""
      Event Service
    
      GET an existing event
        must return status 200                              $get_status200
        must return the event with id = :id                 $get_rightEventElement
        must call findById once on the event repository  $get_findByIdOnRepo
      """
    
      lazy val anEvent = Event(1, "Some Event")
      lazy val eventsBaseUrl = "/events"
      lazy val existingEventUrl = s"$eventsBaseUrl/${anEvent.id}"
    
      lazy val eventRepoMock = mock[EventRepository]
    
      lazy val servlet = new Servlet(eventRepoMock)
    
      def beforeAll = addServlet(servlet, "/*")
    
      def before = {
        reset(eventRepoMock)
        eventRepoMock.findById(anEvent.id) returns Option(anEvent)
        eventRepoMock.findById(unexistingId) returns None
        eventRepoMock.save(anEvent) returns Option(anEvent)
      }
    
      def get_status200 = get(existingEventUrl){
        status must_== 200
      }
    
      def get_findByIdOnRepo = get(existingEventUrl){
       there was one(eventRepoMock).findById(anEvent.id)
      }
    }