Clean Code API – Part 3

24 / 10 / 2021

Read Time 10 minutes

This post, the second in a series, was originally posted on kudocode.me and written by Marius Pruis, Senior Developer at Haefele Software.

UNIT TESTING – API ARCHITECTURE FOR GENERAL PURPOSE

In the first post, we discussed the API architecture from a developers perspective and what is required for a developer to utilize the architecture. We looked at how the SOLID principles applied to the architecture, allowing us to write decoupled units of code and isolating our business logic in smaller well-structured classes.

In this post, we will look at how to apply unit testing by taking advantage of the modularity and isolation of these handlers as well as how to utilize the IOC container for testing.

UNIT TESTING

Unit tests are short, quick, and automated tests that make sure a specific part of your program works. They test specific functionality of a method or class that has a clear pass/fail condition. By writing unit tests, developers can make sure their code works, before passing it to QA for further testing.[1]

AUTHORIZATION FAILURE TEST

clean code 3-1

In the test class above we test the GetLeadDtoAuthorizationHandler mentioned in the developers perspective post. The handler checks if the user making the GetLeadDto request is authorized to execute the request / GetLeadDtoWorkerHandler. The handler requires the user to have two authorization groups, Group A and Group B, to allow the request to continue.

clean code 3-2

The purpose of the test above is described by the parameters of the Run method on the base class. These parameters are used to generate documentation when the tests are executed. When investigating a failing test, the purpose of the test is clear saving us some time with the investigation. A test with an unclear objective tends to be long, difficult to understand and often tests more than one thing.

The run method facilitates the four methods; SeedGivenWhen and Then. These methods contribute to the objective and understanding of the test by confining responsibility to smaller methods.

A test should be deterministic, repeatable and produce consistent results. A test that fails some of the time is as good as no test. Handlers are tested in isolation by managing dependencies through the IOC container. For each test starting up, a new IOC container is created and shared dependencies are registered with the IOC container in the base class. These dependencies, as well as handler specific dependencies, can be stubbed and replaced specifically for testing in the test method. This allows us to fake, stub or mock dependencies like network interaction that can make your test fragile.

clean code 3-3

In our test, we want to inject an Application User Context that does not contain any authentication groups. The authentication handler we are testing requires a user with Group A and Group B access. We do this by calling the RegisterModule method in the base class. By default, a user with both groups A and B is injected, because we register a new module (Module_ApplicationUserContext_With_No_Authorization) the default user is overwritten for this test.

clean code 3-4

The handler we are testing is resolved from the IOC container and executed in the When method. We assign the result to a private variable _getResponse making it accessible to the Then method for assertions. This forces us to focus on the result of the handler and not the implementation of the handler. When the implementation of the handler changes the test is less likely to fail, making the test less fragile. Finally, we have a single assertion to check the response from the handler. Using this test class anatomy, each of the different types of handlers (authorization, validation and worker handlers) can be tested in isolation.

Refrences

  1. https://esj.com/articles/2012/09/24/better-unit-testing.aspx (2019/03/11)
  2. https://stackify.com/unit-testing-basics-best-practices (2019/03/11)
View
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept”, you consent to the use of ALL the cookies.
Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website.

These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.

Necessary

Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.

Non-Necessary

Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.