Why Mocking Is Required In Unit Testing? A Detailed Guide

Unit testing is one of the most vital parts of your entire test suite. We have been putting a lot of focus on this through many of our articles and our overall operating methods. You can know everything about this massive part of the testing pyramid from the article “What Is Unit Testing? Definition, Basics, and Types”. However, we are providing a brief introduction here for your better understanding.

What Is Unit Testing?

Unit testing is the process of testing the smallest bit of your code (also called a unit) in isolation. It is the first, the largest, and a truly important subset of the testing pyramid, the universally followed strategy for efficient software testing. Its primary duty is testing the units of the source code to check if there is any presence of the common issue where the working of every other unit gets affected whenever any change is made to an individual unit. Though modern unit testing frameworks allow one to write test codes in the same language in which the source code is written, the present world is preferring the amazing no-code testing tools like PreFlight as these enable every member of their teams to take an active part in the testing process irrespective of their coding knowledge.

What Is A Unit Testing Framework?

Now, it goes without saying that similar to every other framework used for its specific purposes, a Unit testing framework is developed with an objective to bring ease to unit testing through the creation of test fixtures. Those test fixtures are nothing but .NET classes with special attributes of being capable of being picked up by a test runner.

However, you can perform unit tests without such a framework but in that case, the testing process becomes more cumbersome and loses most of its advantages of being automated. It is your task to understand your needs and choose the most suitable unit testing framework out of the several ones available there. Each one of them has a different set of advantages and disadvantages so the best way to choose one out of many is to analyze what your requirements are and how expert your testing team is to operate the framework. On the other hand, efficient no-code test automation tools like Preflight absolutely demands no expertise from the person using them. Anyone from any team (be it sales, marketing, finance, etc.) can easily create and execute efficient test cases without dealing with even a line of code.

What Is Mocking?

In simple language, mocking is imitating or making a replica of something. But, what is its use in unit testing? Well, unit testing is all about testing an individual unit in isolation but sometimes the units have external dependencies. In such cases, those dependencies are replaced by controlled replacement objects that imitate or simulate the behavior of the real ones. This process of replicating the real environment having actual external dependencies is called mocking. And, now we will dive deeper into it in the further sections.

Till now, we got a generalized idea about mocking but based on various parameters, it is further categorized into these three types that are mentioned below.

Stubs

A stub is an object that returns a specific result based on a specific set of inputs and the best part about using it is that it normally doesn't respond to anything outside the programmed boundaries/limitations of the test. If any unexpected input is provided to it, it generally ignores that or returns the same thing every time. The primary objective of this one is to set up the expected state for the test.

Though using stubs can be really helpful during the early development phase of a system, they bring a massive flaw in the operation. Every test needs multiple stubs of its own to test the different states. As a result, it becomes a repetitive task and a lot of boilerplate code gets involved. To avoid such issues, instead of using only stubs for mocking, an approach of pairing them with test doubles is followed. Stubs can run without a framework but using mocking frameworks speeds up the process of building stubs.

Advantages:

  • As it doesn't need any framework, anyone can easily set it up.

Disadvantages:

  • It may make you rewrite the same code multiple times causing the involvement of a lot of boilerplate code.

Mocks

Mocks are generally described as pre-programmed objects with expectations that form a specification of the calls they are expected to receive. However, for better understanding, these are called sophisticated versions of stubs because they return values like stubs but can be programmed with expectations in terms of how many times each method should be called, in the specified order, and with specified data. They can be described as a replacement object for the dependency that has certain expectations like validating a sub-method that has been called a certain number of times or the arguments that are passed down in a certain way.

Most of the popular programming languages have a lot of mocking frameworks. You can even find them built into the unit test packages of some languages. They are so much preferred because they enable you to write unit tests easily and provide an environment with good unit testing practices.

Mocks have a crucial difference from other test doubles. They do behavioral verification, whereas other test doubles do state verification. The advantage of behavioral verification is that you become able to test the implementation of the system under test as you expect. On the other hand, state verification has no feature of testing the implementation of the system. It just validates the inputs and the outputs to the system.

However, this direct association with the implementation of the system becomes a minus point for behavioral verification as well as for mocks. The primary criteria that unit tests need to fulfill is that no matter what changes are being made to any unit at any point in time, other unit tests must successfully pass. But, if the tests need to be updated for every change in the behavior of the method, there will be a high chance that bugs will get introduced.

Let's understand this phenomenon with an example. Suppose one function of your web page needs data from 4 different databases to operate and you have one test that ensures the smooth receiving of data from 4 databases. After a while, some changes are made such as the API being updated and now it requires data from only 2 databases to operate. But, your test keeps looking for data from 4 databases. To resolve this problem, you need to update your test. As a result, more possibilities of bugs get introduced.

Such problems are mostly caused by the behavioral verification phenomenon that minutely checks the implementation of the code. However, the use of mocking in unit testing that pushes you to write such tests is one of the biggest reasons behind it.

Advantages:

  • The tests become easy to write.
  • These tests allow you to build more testable designs.

Disadvantages:

  • It becomes an inefficient and cumbersome process to maintain the codes because of behavioral testing.
  • Usually requires the installation of a framework. Otherwise, a lot of boilerplate code will be introduced.

Fakes

The objects that replace the actual code by implementing the same interface but without interacting with other objects are called “Fakes”. These have actual working implementations but the shortcuts taken by them make it hard to produce them. Fakes are hard-coded objects that return fixed results. That's why if you try to test different use cases, you must introduce numerous fakes. This phenomenon causes a common problem that if you modify an interface, all fakes that are implementing this interface will also need to be modified.

Let's try to understand it with a common example. Fakes are commonly used in memory databases. They need to save data somewhere between application runs. And, for the smooth execution of that function, you need to design a fake implementation of your database APIs that store all data in memory. These fakes help in maintaining abstraction and keep the tests fast.

Though creating fakes involves more time than other test doubles, it is a more preferred choice for you. Fakes are full implementations that have their own suites of unit tests. Hence, using fakes boosts your confidence. You have the internal satisfaction that your test doubles do not have bugs as they are thoroughly tested even before you use them as a downstream dependency.

Another reason to prefer fakes is that they also encourage the creation of testable designs. But, whereas mocks need frameworks to write their codes, fakes can be easily written like any other implementation class. Though it is possible to include fakes in the test code only, many times they end up being included in the product code. Being held to the same standard as full unit tests, fakes can sometimes even start off in the product code. While writing a library or an API, including the fakes in the product code exempts developers from writing their own mock implementations. And, the best part is that using fakes with such efficient strategies makes the code further reusable.

Advantages:

  • Similar to any other implementation, you don't need any framework for these.
  • You get the opportunity and encouragement to build testable designs.
  • Your code does not stay limited to being test code. It can get promoted to product code. So, your effort will not be wasted.

Disadvantages:

  • The implementation time is higher.

Differences Between Stubs and Mocks

You can clearly see that out of the three main types of test doubles or fake test environments, stubs and mocks are the two most popular ones. But, which one among them is the best suited for you? Well, both stand higher on different grounds. The following differences between them can really help you make your choice according to your requirements.

Base of Comparison

Stubs

Mocks

Implementation

Usually developers or testers implement them or specific tools are used for the production.

Implemented by the developers via a third-party library like Mockito, JMock, and WireMock.

Scenario

Used by uncomplicated test suites.

Used by big test suites.

User Interface

It doesn’t have any graphical user interface.

It has a graphical user interface.

Data Setup

You will find hard-coded data here.

The tests do the data configuration.

Purpose

Stubs work with a purpose of state verification.

Mocks work with a purpose of characteristics verification.

Coupling of Data

These have tight coupling with test data.

These perform well with both tight and loose coupling with test data.

Stateful

No

Yes

Advantages

You will get a lot of free tools and also plenty of resources from the internet.

You will get open-source tools along with plenty of online resources.

Disadvantages

The hard-coded data makes the tests couple tightly.

You mostly need developers to use them instead of letting testers do that.

Usage

Its basic use case occurs if the test data requirements in the suite are simple. 

Its use case occurs if the test data requirements in the suite are complex and also if all the data configurations happen within the tests.

Technical Knowledge

Average technical knowledge is good enough to use these.

Good level of technical knowledge is required to use these.

When To Use A Stub?

For the best results using test doubles, you need to know when you should use which one. The suitable scenarios to use a stub is mentioned below.

  • While developing the back end of a small application with a third-party library to interact with an API, you should implement an interface that will disconnect you from the third-party library. So, here that interface acts as a stub that generates hard-coded values which can be used in a code unit test.
  • During the testing of an independent application, a stub should be implemented for the Rest APIs that form the foundation of the application. Also, you can create those stubs using an internal tool designed by the developers.

When To Use A Mock?

Mocks also have some particular types of use cases that are best handled by them. Let's check them out.

  • If the back end of any application has several classes to test, using a Mockito framework makes it easier to mock the dependent classes.
  • During the development of the back end of an application, if you need to disconnect from the API dependencies in HTTP, using a mock framework like mountebank or WireMock is the best choice for you. Then you can proceed with creating a mock for the dependency classes in the test.

General Types of Mocking Frameworks

Though for small to medium codebases, most developers prefer writing the test code by themselves for better control over their code, for testing larger applications, mocking frameworks help a lot by providing access to large libraries and other efficient features. There are numerous mocking frameworks available in the market so you have a lot of options to choose from. Here is the general categorization of mocking frameworks so that you can decide which one will properly fulfill your requirements.

Proxy Based Frameworks

The name clearly shows that these frameworks are all about using duplicates instead of originals. Any object that is used as the replacement of an original object, is called a proxy. Similarly, in this method, when the proxy is called, it can decide from the following options how to respond to that call.

  • It can delegate the task to the original object.
  • It can handle the task by itself.

If the proxy handles all method invocations itself, there will be no necessity for an instance of an interface/class. Some popular proxy-based mocking frameworks are EasyMock, JMock, Mockito, etc. Let's understand their operation with an example.

Mockito.mock(Welcome.class)

Here, this code works for creating a proxy for the “Welcome” class.

Limitations of Proxy:

Though proxies are amazing mocking frameworks, they have a few limitations too. Along with the inability to build a proxy for a final class, you cannot also intercept -

  • Static method calls.
  • Private method calls.
  • Final method calls.

Byte Code Manipulation/Classloader Remapping Based Mocking

As the name suggests, the primary step in this method is telling the class loader to remap the reference to the class file it loads. Let's get a clearer idea through an example.

Suppose you have a Hello class with the corresponding .class file named Hello.class. Now, you want to mock it to use MyMock as a replacement. To do that using this type of framework, in the classloader, you have to remap the reference from Hello.class to MyMock.class.

This type of framework provides you with the ability to mock objects that are created by using the new operator. As a result, you will feel more power than you feel while using proxy-based ones.

Some popular classloader remapping-based mocking frameworks are jMockit, PowerMock, etc. And, the best part about using these amazing frameworks is that you can get rid of the limitations that you experience with the proxy-based frameworks.

Conclusion

After all these discussions, you can clearly see how important it is to use test doubles. Mocking is a truly necessary process to maintain a healthy test suite. However, while using mocking frameworks or test doubles, you must pay attention to the consequences of using a mocking framework from the beginning. Using a mocking framework is really helpful but if you feel like using it has become essential, know that your code itself is not abstracted enough.

A better practice is starting with a mocking framework and eventually creating more fake implementations by yourself. It leads you to have a healthier code and you will not end up wasting your time relying too much on mocks or testing the unnecessary things.

However, the modern world wants to achieve the best results within the least amount of time. That is only possible by using efficient no-code testing tools and Preflight is one of the most renowned ones. Our simple yet highly effective browser extension lets every member of your product team equally contribute to testing every kind of tech product. No matter if anyone has coding knowledge or not, he/she can create and execute test cases with just some clicks. You just need to start experiencing it by booking a demo.

For more information, you can always log on to our amazing website. And, if you are looking for some awesome tech articles, our blog page is a must check out for you.