Unit testing is often discussed in software development. When it comes to writing functions, we tend to think about how we can integrate that function in the entry point of the application. When it comes to unit testing that function, having all branch coverage inside that function can be hard and tricky.
Akka HTTP has a helpful testkit to test routes that make unit testing simple. Testing without TCP connection should not be complicated. You can mock the IO function, such as
singleRequest. The key to a readable and accessible test suite lies in how you structure the functions.
In this tutorial, I want to share how you can structure the Http
singleRequest function in Akka HTTP so that it is easier to mock and get 100% branch coverage.
In this tutorial, I use a fake response of a simple dummy rest-client which retrieves dummy employee information. The task is to create a client service that gets the employee information through Http SingleRequest and parse all the fields in the response. Usually, we write a function like this:
The function creates a fetch call through
http().singleRequest and deserialize the JSON string to an Employee model class with Circe.
This function becomes hard for unit testing because the IO function coupled with other operations in the function.
To make the application as loosely coupled as possible. We can wrap the IO function into a trait so that the functionality can be overridden or mocked.
In this case,
http().singleRequest is the component that creates IO. Therefore, we can extract the functionality out and wrap it into a trait.
Because we extracted the
singleRequest as an abstract function, we can mock the function in the unit test with Scalamock.
Order of Execution
We can write the regular implementation first; then we can refactor the function by extracting the
We will use Circe to encode and decode JSON String, and Scalamock to mock the IO functions.
Let’s create a domain model Employee:
Once we create a domain model, let’s create the main class. Writing the main class helps you design how you want to write the function. It also helps to understand how another caller uses your function. Therefore, the main class is a simple way to test the function.
Inside this function, we extract out the fetch endpoint URL by wrapping it with the
Let’s refactor the code so that
handleRequest can be mock.
Let’s create a trait, HttpClient, that has an abstract function
sendRequest. Then, let’s mock
sendRequest later in the unit test.
Then, let’s use self-type annotation in the
EmployeeRestClient trait so that any instantiation or any class that extends the
EmployeeRestClient also need to extend the HttpClient trait. This is very useful for later when we instantiate the service.
Then, we create
ClientHandler trait which contains the
http().singleRequest Function. We will not test this trait, but this trait will be mocked in the unit test later.
This refactor separated JSON deserialization and
singleRequest. Then, we can test
getEmployees, by mocking the function on
Let’s set up all the mocks.
Since we extract
httpRequest, we can create another trait that also extends
HttpClient and mock the function.
You can specify the expected input and returns on the function.
Scalamock automatically mock the function for you. Therefore, no side effect or IO involved in running the
Once we mocked the function, we start setting up the test suite. The test suite involves a setup environment, mock expectation, and assertion. Once we create an expectation for the function in ScalaMock, it also creates an assertion. If there is a discrepancy between the function call and the expectation, that test suite fails.
Did you notice that the test suite is precisely like the Main function that we defined earlier?
Starting from the Main function makes it easy to construct the test suite.
The difference between the main and the test suite lies in the different
ClientHandler traits. In main, we extend
ClientHandler. In the test suite, we mock the
By doing this, we can test
getEmployee functionality because we wrap the
http().singleRequest separately into another function.
- Design your function from the main class
- Refactor your design by extract the IO portion of the function for mocking later.
- Mock the extracted IO function with any mock library you want, in this case, Scalamock.
- Construct your test suite like how you would call your function from the main class
That’s it! I hope this helps you write better unit test in your Scala projects or unit test
HttpClient request. The full implementation of this tutorial is on GitHub. Please comment below if there are any questions and comments. If you have any other great strategies, feel free to share your knowledge in the comment section below!