SDET: Controller and action layer concepts in API test design

Kostiantyn Teltov
7 min readDec 13, 2023

QA Avengers, greetings!

Today is without long-reads:)

API testing holds significant importance within the testing strategy, often considered a crucial practice that should take precedence over higher layers of automation testing. It’s a well-established best practice for API testing to assume a leading role and be predominantly automated. Moreover, the API test layer presents itself as an ideal foundation for building upon higher layers of test automation.

Everything said above means that you need to build a good foundation for your test automation solution. As you may understand from the article name, today we are going to look at test automation solutions from the API layer's perspective. I’m not going to reinvent the wheel. Similar layers already exist for application design. You should understand that this will be a high-level concept. We will look at some basic examples, but in the real world, you may want to do more and split on some other parts.

As usual, first let’s jump to the problems, and then layers can resolve these problems

QA Assemble!!!

Problems and layers

When we design our API tests we usually start from the Client that helps us to make some operations. These operations help us to work with a server.

And good practice is to start with Controllers.

The Controller layer, in the context of API testing, is a lower level layer responsible for handling incoming requests from clients. It acts as an intermediary between the incoming requests and the business logic

In simple words, it helps you to send requests to the server and receive responses. Maybe also convert/deserialize those responses to some models.

In the example below, you can see the use of the Posts controller request, which sends a POST /Posts request with a body and deserializes the response to the expected model.

Short implementation details will be considered in the concrete section.

But the general idea is that you will use such controllers when you need to build specific API test scenarios. In simple words, when you need to create step logic to test your API endpoint in a different use case/test case.

This approach gives you different ways of testing your API. But let’s say you want your API to behave like a precondition. It could be both a precondition for API tests and a precondition for UI tests.

In this case, our API call or set of calls acts as business logic and we may not need to specify all the details. We just need to create a new post and use it for further testing. This one layer we can call the “actions” layer. There is no point in arguing about names. You can call it the “business” layer if you like. The important thing here is to understand the idea.

The Action layer, also referred to as the Service or Business Logic layer, represents the part of the application responsible for implementing the actual functionality or business logic associated with API endpoints.

In the example below you can see the creation of the new POST, but we hide the implementation details because in this case, we are not interested in specifying those details. Or maybe we can specify some interesting things, but we don’t need to fill all the fields.

This was a very simple example. It does not even contain any parameters. In real life, there may be a method with the parameters you need to specify. In this article, we will focus on a structure concept rather than implementation details.

Perhaps you already understand the concept. But let’s dig a little deeper and look at each of these layers separately.

Controllers layer implementation

Think of this layer as the Lego bricks in your test solution. So you need to build a box of Lego bricks.

Where you place these layers is up to you. Usually, it will be in the Controllers folder or a specific project. The most important thing is to keep it separate from the tests. Ideally, this layer will only be used by a specific project. Let’s say you have an authentication microservice. Ideally, this layer should only be used by the test project that is responsible for testing this microservice. If you need to use some logic inside the other microservice, it is probably a good candidate to be part of the actions/business logic layer.

In the example below, I have simply created a separate API.client project and controllers under request builders. In this test example, I decided to use the Builder pattern because I like it a lot. But these are more implementation details. That’s why I named my folder “RequestBuilders”. But potentially it could be called Controllers.

Within this folder, we have other folders with specific implementations. For example, our Posts requests.

This article is not about implementation details, so I just briefly showed you the code inside. Again, the general idea is that this layer is a box of Lego bricks. It should allow you to build everything you want from scratch. Maybe not everything, but it should be extensible. This means that you can always add new blocks to achieve new building goals.

Actions layer implementation

If the control layer is a box with small blocks, then the action layer is a set of prepared castle pieces. Like towers, leaving blocks, and so on.

In our case, Castle is the whole project:)

Again, it is up to your project how to separate it from the rest of the code.
You may want to create a separate project or just a folder.
I created a separate folder for the training project.

Inside this folder, you can see a class that is responsible for the Post actions. This class contains two examples of action methods. One of these methods creates a post and the other deletes a post. Under the hood, I just reused the already existing controller constructor. In simple words, I reused Lego blocks to build some towers.

If you look closely at the implementation of the methods, you may notice that we not only specify some default action values but also check that the response was returned in the expected state. This isn’t always necessary, but in my case, I don’t want to continue if some step has failed.

So, to put it crudely, the actions layer is just a wrapper around controllers. Anyway, you should remember that these wrappers can be used not only for your project. You may want to expose them and use them in some other test project. So it makes more sense to separate it from the test project. It may belong to several test projects.

The important thing to say here is that you may have more complex actions. For example, if you need to have a chain of API calls to achieve some business logic.

In the end

Let’s structure everything in a few short words.

Controllers — Classes and methods that help you to send requests and receive responses with a wide number of possible options. Usually belongs to a specific microservice test project. We always start design from these classes and methods.

Actions — Wrappers around the Controllers that help to encapsulate and simplify part of the API calls logic. May be used by multiple projects and you should remember about it during design. Usually concreate business logic implementation.

Know you know basic concept. It is up to you hot to split and structure it in your project/solution.

Thank you for reading. May you build the best test automation solutions!

--

--

Kostiantyn Teltov

From Ukraine with NLAW. QA Tech Lead/SDET/QA Architect (C#, JS/TS, Java). Like to build testing processes and help people learn. Dream about making indie games