When we unit test an application we are testing its components. This means that one test will test one component in an class/object/trait and its outcomes. If a component utilises other components of the application we would mock then out using something like Mockito.
Integration testing on the other hand will wire the application components together and tests that the application works as a whole. We still want to only test the application though. Therefore we would mock out calls to external services using something like wiremock.
In this gist we have a slice of scala play application defined; a routes file, a controller and a mongo repository. We want to make sure that this slice of the application can have the url called and from that return a 200 status code with a json body by calling down through the route, the controller, repository and through to mongodb.
If we take our application slice we can try and decompose it into a set of preconditions. Looking at the code comments in UserAccountRespository.scala we can see that...
- There is a call out to an external service to ensure the user is authorised
- There is another call out to get the users id based on their current session id
Since we are looking at a happy scenario we can also say that the user needs to exist in the database before any of this can happen.
If translated into Gherkin...
Feature: A call has been made from the frontend to fetch details of the user by retrieving their user account
Scenario: Call has been made to get the users account in json
Given the user exists in the database
And the user is authorised
And the users id has been fetched
Then the response has a 200 (Ok) status
And the response body contains the users account details
To make our test understandable and maintainable we are going to capture this gherkin in scala.
Looking at IntegrationUtils.scala we can see a trait that has been setup to include WireMock components and UnitSpec. The special part of this trait is the PreconditionBuilder class.
The PreconditionBuilder class is going to create something that resembles Gherkin syntax and therefore make our integration tests readable. The other important part about this class is the implicit builder on the first line of the class. This val is what lets us create a precondition statement.
Lets get started...
Precondition 1 says a user must exist in the database before the action can run successfully. In the utils trait there is a case class called Mongo that takes an implicit builder of type PreconditionBuilder. Contained within this case class there is a function defined as hasExistingUser. This function would contain code to actually insert a user in json format into the database. An important note is that the function returns a the implicit builder, this is so we can link preconditions together. Finally a function defined as mongo has been created in the PreconditionBuilder class that created the Mongo case class.
Precondition 2 says a user must be authorised to perform this action. This is setup in an identical way except that the case class is called User, the function inside the case class is called isAuthorised and the function in PreconditionBuilder is called user and that returns the User case class. Another difference is that following preconditions will use WireMocks stubbed outbound calls to determine if the user is authorised and that the user is has been fetched.
Precondition 3 says the users id has been fetched. This time the case class is called SessionStore the function contained with is called getUserId and the function in PreconditionBuilder is called session
The final part of this PreconditonBuilder jigsaw is the function called given and this where our precondition statement starts.
With this, the Gherkin syntax featured can be captured in scala as...
given
.mongo.hasExistingUser
.user.isAuthorised
.session.getUserId
The next step is actually call the apps get user account url and make assertions based on the scenario you're testing. To do this you can use whenReady contained with the ScalaFutures trait. An example of this usage can been seen in the GetUserAccountISpec in this gist.
When using WireMock to stub outbound calls, the stubbed get, post, put and patch commands can get very lengthy depending on what json you're getting or posting. This way of building integration tests means that you can reduce how much code is in your test therefore making them readable and understable. It also means that you can easily reuse database calls and WireMock stubbed calls as they're contained within the PreconditionBuilder code and not the body of your test.