Mock and Stub
The art of faking
One of the important aspect of testing is test isolation. A test isolation is where each test is isolated into its own environment. Even if the component is dependent to another component, it is always the best to break those dependency. We can achieve this by using mocks and stubs.
1w + 0h, why
The more we develop, the more complex our code get. We are also encourage to develop our code lowly coupled, but everything has a lower bound. At some point, our code will have a decent amount of coupling and nothing we can do about it. Many problem will definitely arise from that, one of them would be testing complexity.
When we create a test for a component, that component may dependent to other component. This makes us need to deal with both component. The other component may need special attention like a setup or a state that is supposed to be set by other other component.
Many testing technique can save us, such as mocks and stubs. Both of them serve the same purpose which is to fake a dependency that we don’t want to include in our test. Here I’ll elaborate more on them.
Mock
The picture above is a good example of mocking scenario. There are three main component that is highlighted here. The security component that we want to test the behavior and two component, window and door component which is the dependency to our security component.
We want to test the behavior of securityOn
function. The intended effect of the function is that the window and door object should be at the closed
state after the function is called. Rather than using a the original object that may require additional step such as setup, we can use mock to fake out those classes.
In the image, you can see that windows and door object has been replaced with a mock object. Since we don’t care the behavior of those object other than it’s closed state, we can just create a mock for that specific behavior. The door and window mock handle the function close
, tho it ignore the original implementation, just straight up set the closed
variable. A mock is not expected to return anything since it is the behavior to that mock that we want to test.
We can verify whether the function ran correctly by asserting the closed
variable inside window and door mock object.
Stub
Here again, an image of a really good example of stub. The main difference between mock and stub is what part they want to test. Mock is used to help test an object we want to test the the mock itself usually is not expected to return anything. A stub on the other hand, will return something that is going to be used by the component.
Stub is usually used to replace a function that is that will return some kind of data. In this example, an class GradeSystem
is used to fetch some data from the database. Since it is a best practice for a test environment to not interact with a production or even a staging environment, we need to make a stub out of it.
Since we don’t care how the GradeSystem
get the data, we can create a stub from it. We just want the class to return a specific value to control how the test go. For example if we want to test the the averageGrades
method, we can make the GradeSystemStub
to return a proper value, then assert the result from the averageGrades
function.
Another example of a the use of stub is when we want to test the behavior of the error handling. Since the original grades
function is an async function, there may a case where the connection of either end is having a problem. We can create a test environment where the stub will return some kind of error such as a rejected promise. Then we can test the behavior of the GradeSystem
class.
Here is an example of a stub. I have a class that is job to to fetch some data from an API with a help of a library. I don’t want the library to interact with the production server, and also the library itself need a setup beforehand. That’s why it is better to mock the library so that we can control the flow of the test. I set the return value of the library’s function with a resolved promise. Then assert that the library’s function have been called. Note that in this instance, I used a spy, but behind the scene it, is a partial mocking, and the returnsValue
function create a stub of the function.
Expect
There are many more testing technique to be learn. This is just some technique I personally have used. I hope in the future I can learn more of those technique, that will help me in the development process. Thank you for reading!
Update
A further analysis of mock and stub using the SWOT analysis.
Strength
Mocking can save a lot of developer’s time. When we are writing the test, we only need to focus on the object we are testing. Say our object require a another library that we don’t write or we never used. Learning those dependencies can take some times, worse can happen if we don’t understand the dependencies, use it, and create a bug instead which require more time to fix.
With mocking, we only need to modify it’s state to what we expect. Stubbing also simple, we just set a return value that we want.
Using a test double framework such as Jasmine that we use is neat. It saves us from code repetition and human introduced bug. Primarily with mocking, the code size difference can be substantial, depending on the context. If you understand how a test double work, it is highly advised to use a framework.
Weakness
Using a framework do comes with some downside. For us as a newcomer to JavaScript environment we need to learn about many framework, the front end framework, test framework, and the test double framework itself. For our project which are a one time thing in a limited time, learning these frameworks can take times that we instead can spend on more useful stuff.
If we decide to not use a framework, there will be many code repetition introduced. So its a dilemma that you need to decide.
Opportunity
A general understanding of test double and it’s framework create many benefits. Even though I stated above about the downside, I would still encourage learn about test double technique and specific framework that your project can use. Eliminating code repetition and human error on fundamental basis will speed up your development cycle by a huge margin.
Threat
Excessive test doubles can impact your performance by a quite huge margin depending on your code. If you are using a CI flow on a platform like GitLab CI/CD, there is a monthly time limit for your runner. The longer your test takes the more it consume your monthly quota.
TL;DR
- Good programming technique, worth learning for future.
- Learning a framework takes time, but can save you a lot of time and avoid code repetition.