Ruby RSpec: Mocks, Doubles, and Spies with Constructor Injection
Get link
Facebook
X
Pinterest
Email
Other Apps
-
Inversion of Control (IOC) containers or Dependency Injection is a common design pattern used to help engineers "inject" different instances of an object at runtime into services, controllers, or gateway classes. This concept is somewhat foreign to the Ruby on Rails community but valuable in some contexts. Most unit tests in RoR won't utilize mocks and stubs and instead opt for direct integration with the database. This article provides an alternative to integration testing by unit testing through constructor injection.
In this example, we will test the business logic that operates between an API request and an external, third-party web service.
I will note that this pattern forces developers to consider not using patterns like that in active record callbacks. The problem with callbacks is they are hard to test and can conflate what business rules exist in a domain context. Instead, you can move these operations into "service" classes. This enables future developers to better see the workflow and lifecycle of a domain model and its dependent resources. It forces you to be more intentional and ask what am I trying to solve and how can I communicate this through code enforcing patterns and succinct testing.
Song Model
This model represents a song object that can encode and decode to/from JSON.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This class instruments the business logic and handles interactions with the external API using a "Gateway Service."
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This API (gateway) service enables us to proxy calls to the external API. This instruments the HTTP interfaces and allows us to mock these calls during testing.
Dependency Injection
If you notice in the song service, we "inject" the gateway service by passing it through the initializer. This is called "Constructor Injection."
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We can then override the gateway class when testing the song service by injecting a mock or stubbed version. This allows us to avoid implementing the HTTP endpoints enabling a unit level of testing.
Testing with Mocks
When writing tests, we can inject a new implementation of the gateway service. One way to do this is to implement a new class that implements all methods within the scope of the test.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RSpec provides a "double" interface that can stand in for any object in the test cycle.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RSpec also provides spies, a form of double where you can "spy" on the object interactions. This is my favorite of the three because you can verify how many times the doubled object method was called.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Rails is a great framework, and the ruby community could learn from other web frameworks when enabling and optimizing for better testing. Constructor injection is one tool that may be useful in your testing toolbelt and in improving the structure and readability of your code.
Since writing this guide, Spring has come out with their own Comet and Websocket implementations. I suggest you first read through their guides before deciding on atmosphere as your Comet & Websocket solution. 1. Messaging with Stomp over Websockets 2. Spring Messaging On to the guide... Feel free to clone the source of this project on github. springMVC-atmosphere-comet-websockets The Websocket protocol is defined by W3C here . The protocol is considered an HTTP upgrade protocol. Essentially what happens is, a websocket request is made by a client browser, via the HTTP protocol. Once, the request is in, the server should respond with a success. Then the browser and server establish a TCP link to handle communication from there on out. This protocol enables bi-directional communication between a client and server. Here is a link to browser and servers that support Websockets and/or Comet: comet/websocket support link
It’s been awhile since my last blog post, and it’s been for several reasons. The primary, is that our team has been challenged to implement a Microservice Architecture (MSA) that fit’s my current company's needs. It’s been a process defining the stack, learning the new stack and providing tooling/components for developers to innovate.
When it comes to common tooling in Java, Guava is your best friend. If you are still using Apache Commons, stop now. Below is the gradle dependency information for guava as of this writing.
Comments
Post a Comment