Why Unit Testing Matters (We Even Developed a Unit Testing Strategy for Restful API To Prove It!) 

Ensuring your code works properly is critical for the happiness of you and your customers and the success of your business. The key to code that works is unit testing. According to one useful definition:


In computer programming, unit testing is a software testing method by which individual units of source code are tested to determine whether they are fit for use. A unit is the smallest possible testable software component. Usually, it performs a single cohesive function. A unit is small, so it is easier to design, execute, record, and analyze test results for than larger chunks of code are. Defects revealed by a unit test are easy to locate and relatively easy to repair.


There are no shortcuts to successful software development. Like anything, you get out what you put in. At Integrant, we know this lots of experience. After 28 years in the industry, we have faced many situations where we couldn’t have delivered the quality our customers deserved without proper unit testing. We know the arguments and pros and cons well; some insist that unit testing is too difficult, time-consuming, and expensive to properly implement.


We take the opposite view. We remember plenty of situations where unit testing saved us endless hours of work and frustration. When we onboarded a client in the food processing and manufacturing industry that needed to build a mobile application for their sales team, the project was behind schedule and over budget.

It was clear there were no margins for error in getting the code right.


By leveraging our supply of fully trained, specialized engineers from our 4Plus1 Shadow Engineering Program, we applied hands-on methods in agile best practices and unit testing to guarantee a platform that worked successfully and exceeded the client’s expectations. By going the extra mile and nailing this initial project, we locked in on continuing opportunities with this client over the past 4 years.  

After seeing the benefits of unit testing again and again, we needed a way to make the process as streamlined and efficient as possible. That’s when we envisioned building a unit testing framework for REST API.


In this article, we describe the details around this project and how we have successfully implemented a solution that makes unit testing easier to learn, more operationally efficient, as well as more reliable and cost-effective.  

The Big Picture (Why, How, What

One of the biggest drivers for this project was to build a unified infrastructure for applying unit testing to APIs. We realized after years of trial and error that there were no shortcuts to making quick code changes other than through what one engineer has well described as “rigorous and automated tests done the right way.” We therefore identified two primary goals around the importance of unit testing:

  • Develop an efficient way to teach and train our team members, especially in the 4Plus1 program, to do unit testing quickly and efficiently.
  • Build a vision and strategy for how to apply unit testing to the REST API in a manner that abstracts the database layer and uses the entire framework as a data access point.

The key to approaching this complex task was to develop a framework for unit testing that fulfilled three things:

  • Avoid any code refactoring
  • Abstract the database layer
  • Establish an isolation framework

Following these action items and guidelines enabled us to successfully kickoff unit testing in the first API in the project. By applying unit testing to several layers independently, we were able to prove the value of this automated approach. This demonstrated why an API-driven approach to unit testing our client architectures would not only save time and costs but dramatically improve overall reliability and efficiency.  

Towards a Unified Testing Strategy 

The major driver for automating our approach to unit testing was to help our teammates kickoff new software projects on the right foot. We wanted to empower our workers with a set of best practices that would enable them to work faster and more productively.


By adopting a unified unit testing strategy, we needed our teammates to feel more empowered to use reusable components in their unit testing process. This would produce consistency in our code base across various APIs. But ultimately, our aim was to produce a cultural mind shift so that unit testing functions as an integral part of the overall software development lifecycle and not just a last-minute add-on.   

We help our clients create impact across many industries. See how on our case studies page!

The “Onion Architecture” to the Rescue 

The ability to do unit testing for each layer separately without the need for code refactoring relies on an approach described as the “Onion Architecture.” Introduced in 2008 by Jeffrey Palermo to improve testability, maintainability, and dependability, the Onion Architecture addresses the challenges faced with 3-tier and n-tier architectures, and to provide a solution for common development challenges.


As Palermo explains:

“The main premise is that it controls coupling. The fundamental rule is that all code can depend on layers more central, but code cannot depend on layers further out from the core. In other words, all coupling is toward the center.”  

The advantage of the Onion Architecture for unit testing is that it creates a strong separation of concern (or abstraction) between the different tiers of the platform, which ultimately reduces the likelihood of testing errors. By decoupling between different tiers, developers can easily go in and test a target class without the likelihood of interfering with other parts of the code base.


The Onion Architecture can best be illustrated in the diagram above, which shows how a data layer is only accessible or visible to the one layer above it – just like an “onion.”


As you can see, “Repository” is only accessible to “Services,” which in turn is only accessible to “Controllers.” From a unit testing standpoint, this is very important. By using interfaces, the ability to mockup and replicate certain coding dependencies becomes much easier and reduces the likelihood of errors in the testing process.


When aligned with the SOLID principles of object-oriented design, the Onion Architecture creates a powerful framework for unit testing that saves money and time across the software development lifecycle. 

Leveraging the Moq Framework for Database Abstraction 

To write our unit tests efficiently and independently without code refactoring, we adopted the open-source Moq framework for .NET. By creating what are known as “Mocks,” developers are able to test each code layer independently without the risks that often accompany unit testing.


As Molly Alger explains:

“Mock objects allow you to mimic the behavior of classes and interfaces, letting the code in the test interact with them as if they were real. This isolates the code you’re testing, ensuring that it works on its own and that no other code will make the tests fail.”

With Mocks we were able to mock our component dependencies and apply the “dependency injection concept” in a way that reduced the chance of unit testing errors, increased efficiency, and saved costs. Definitely a win-win for the client and for us!


In addition to our isolation framework, we needed a way to abstract our component dependencies such as third-party mappers in order to expedite our unit testing process. Since we adopt AutoMapper as our baseline tool for object-mapping, we were able to wrap this tool into a customized service offered through a convenient user interface. Now the AutoMapper library was easily available for use or to be mocked or replaced anytime by our engineers to streamline the testing process.


In addition to AutoMapper, we applied the same abstraction principle to our database layers. According to Microsoft, “When writing tests for your application it is often desirable to avoid hitting the database. Entity Framework allows you to achieve this by creating a context – with behavior defined by your tests – that makes use of in-memory data.” By adopting Microsoft’s best practices, our team was able to write our own in-memory implementation of the database components without touching the live database.  

For example, the following diagram shows an example of using the Moq framework to construct an implementation of in-memory representation for the entity named “ResourceGroup.”

 

To achieve this, we adopted helper classes designed to implement a number of interfaces that in turn setup various functions to be queried against. By handing over the in-memory data to the Moq context object, we were able to initialize the dependency injection container and register this fake Moq database context as a dependency for the repository. 

 

The key takeaway here is that by creating this “Mock,” we abstracted away the database and tested it against the in-memory collection without modifying the actual database. Another approach to creating in-memory doubles with the Moq framework is to write them by hand but this requires a lot of manual coding, which is obviously much more time consuming.   

Looking for more than software testing? Check out what we can offer you! 

A Successful API Build Pipeline and Extensions 

By adopting the Moq framework and producing “mocks” of the in-memory collection, we were able to create a unit test project based on the NUnit open-source unit-testing framework for .NET applications. We adopted the Resources API pipeline (following the entity, “ResourceGroup”) to execute these unit tests.


The key here is that the build pipeline would not execute as long as there was any fault within executed test cases. In other words, a successful API build pipeline was our assurance that each individual test worked properly.


Another output we achieved is the ability to use infrastructure classes for testing other APIs in the project that rely on the Entity Framework for data access. This optimization will enable us to easily scale our unit testing efforts to other levels of client architecture within our software development lifecycle. 

More Unit Tests 

To further assess the scope and success of our unit testing project, we wrote API tests across three primary domains (based on the “Onion Architecture” described above):

1. Controller level unit tests

2. Service layer unit tests (located in the business layer)

3. Action filter unit tests (used for validating incoming HTTP requests)

For example, let’s run through a quick example from the services test. First, we have managed to abstract away the database so the access layer works against the “Mock” in-memory collection. We also have a function responsible for retrieving data from the ResourceGroup entity according to specific search parameters. There are three objects in the in-memory data collection and our unit test asserts that the function should return those three objects, based on the diagram below.  

Sure enough, once we run the unit test, we find that the function returns three objects.

The unit test, in other words, shows that the output matches what we would expect in real-time. By isolating the framework using Moq and abstracting the database with in-memory doubles, we succeeded in writing tests within the service layer without touching any dependencies.


Indeed, we have developed a more unified testing strategy that meets our vision and goals for efficiency and reliability established above. 

Our engineering teams are here for you! Reach out at any time to get started on your next project.

Takeaways

At Integrant, we’re passionate about developing elegant applications that far exceed our customers’ expectations. Anybody can be average! We aim to provide superior service and results. To achieve that, our code must work in real-time, every time. In order to ensure the reliability of our code, we are big practitioners of unit testing.


In this piece, we have described our process of building a unified testing strategy that avoids the time-consuming process of code refactoring by using the Moq framework to isolate the code and abstract the database and create real test cases. This enables us to not only save lots of time from manually writing test code but reduces errors and ultimately saves costs – a win-win for our customers and us.


Are you searching for a reliable partner to lead you on your digital transformation journey? Do you have an application that you want to bring to market, but just haven’t found the right resource? We can certainly help! Integrant brings nearly three decades of deep industry expertise to the table to ensure your project is delivered beyond your expectations. We’re obsessed with making our customers happy; in fact, if you choose to work with us, we’ll stop at nothing to overdeliver on value.


Give us a call today to schedule a demo and find out how our reliable team of expert engineers can bring your business vision to life.


Subscribe below or contact us to see the difference we can make. We can’t wait to get started! 

Related Content

What are the essential elements of a great test automation framework? In this post, Integrant’s team discusses the factors that are crucial for success.
We developed a custom test automation framework for a life sciences company to ensure every dev team was in alignment, and you can read more about it here.
Subscribe to our newsletter!

We've been in the software industry for  30+ years so we have a lot to share with you!

Follow US

Address: 16870 W Bernardo Dr, Suite 250

San Diego, CA 92127

Email: info@integrant.com
Phone: +1 858.731.8700

© 2021 Integrant, Inc. All Rights Reserved