Fluent Interface Design | Bondar Academy
Course: Playwright API Testing with TypeScript
Module: Building a Framework
Instructor: Artem Bondar
Lesson Summary
In this lesson, we focused on building a custom request handler for managing API requests in Playwright. The motivation behind this is to improve the readability and maintainability of API scripting, which can often appear bulky and hard to follow. Key Components of API Requests Every API request consists of five main components: URL Path Headers Parameters Body Unlike tools like Postman or frameworks like Rest Assured, which separate these components, Playwright combines them, making the code harder to read. Creating the Request Handler We started by creating a new folder called utils and a file named request-handler.ts . Inside this file, we defined a class RequestHandler with methods to handle each of the five components: URL - type: string Path - type: string Params - type: object (JSON) Headers - type: object Body - type: object Each method saves the provided values into private fields of the class, allowing for reuse later. Fluent Interface Design We implemented a fluent interface design by returning this at the end of each method, enabling method chaining. This allows us to call methods in a more readable format: API.url('example.com').path('/articles').params({ limit: 10, offset: 0 }); Conclusion By the end of the lesson, we successfully created a structured approach to building API requests, enhancing the clarity of our tests. Future lessons will focus on further developing the request handler.
Video Transcript
All right, guys, so let's begin, and in this lesson, we're going to build our custom request handler that will be responsible for building an API request for us. So why do we need that? Remember before, when I shared my opinion about how Playwright scripting is implemented, it looks busy and bulky, and it's hard to read and so on. Why is that? It's because the all components that are needed to perform API requests, they are kind of all together, so think about it. Every API request has basically five components, is URL, path, headers, parameters, and body. And in API scripting with Playwright, we kind of put those things together, and when you look at this code, it's just hard to read. Why is it easy to use Postman? Because in Postman, all those five components separated. Why is it easy to use, for example, Rest Assured or Karate frameworks, quite popular frameworks for API automation, because they also separated those responsibilities, those five different components into the different methods or commands. So that helps to script easier, read the code, maintain the code, and so on. And this is something that we're gonna implement in Playwright right now, at least start building, and we'll improve this structure later inside of this module. All right, so let's move on. So here in our project, I will create a new folder, and I will call it utils, utils. I will keep this folder for all helper functions and classes and something that will accommodate the test execution. And inside of this utils folder, it's empty, right? Create a new file, and I will call it request-handler.ts. This will be a main file responsible for all requests, for all API requests. Inside of this file, create a new class, export class request-handler. And I remind you that class name always should be from the capital case, okay? And inside of this class, now let's create methods that will be responsible for all those five components for our scripting. So what is that? It is URL, path, then it is params, then headers, and body. All right, so using these methods, we will collect from the test body all those components independently to perform API requests. So let's add parameters inside of the methods. So parameter URL will be a type of string. Path is also, let's create path is type of string. Parameters, let's call it params, is the object because we will pass parameters in form of JSON object. I remind you the parameters is just key and value pairs. And for the key and value pairs, very convenient to use JSON objects. For the headers is also object, and for the body is also an object. So what are we gonna do with those values? So let's save the values collected from those methods into the fields of the class. And then we can use those fields later for some other coding within the same class. So let's create a private field with the similar names. For example, base URL and the type of string. Then private API path type of string, and let's assign a default value of empty string to this path. Then private next is what query params type of string. Object and assign a default value of empty object. Then private next is API headers is object as well and assign empty value of the object. And the last one private is API body is also object and assign the empty object. And now let's assign the value that we pass as argument into the method to a respective field. So the value that we pass through the URL, we should assign to base URL. The value that we pass as a path, we should assign to API path and so on. So we technically aggregate or collect the information from the test body, saving it into those fields. So we can reuse it later in the test. And let's do this. This.baseURL equals to URL. And this is the syntax how it works. URL is the parameter of the method. The value passed from the test will be assigned to this parameter. And then the value of the URL, we assign to the field inside of the request handler class. Keyword this is used when you want to refer to the fields or methods within this class. So this essentially returns you the instance of the same class that you are working in. And this is the correct syntax, this.baseURL, or you can call this.path or something. You can get access to the fields or methods through this. So let's implement the same thing for the other methods. And I type this API path equals to path, then this query params equals to params. This, what's next? API headers equals to headers. And the last one, this API body equals to what? Body, equals to body. All right, so this is done. What is next? When method is called, we want to return the same instance of the class making like this, return this. So this is called fluent interface design. By returning this at the end of the method execution, we provide access for this method to other methods inside of this class. Private will not be available, but other methods will be available. So using this approach, we'll be able to chain our methods one by one using just dot notation, URL.path.params and so on. So let's add this return, this as a return statement for each of our methods. Return this, return this, and return this. Okay, so the very basic structure is done. Now let me show you how it's gonna work. So inside of the test, so inside of the test, let me create a new file. I will keep example spec.ts as just our reference point. We will start a new test. I will call it, for example, smokeTest.spec.ts, something like this. So I will take import from the example.spec.ts and start a new test. First test. And now let's use this request handler inside of our test. How to do that? So I create a new constant, let's say API, and then I call a keyword new to create a new instance of the class. So request handler, and here we go. I see this pops up in Visual Studio Code. I hit Enter, and look, the new import was created automatically by Visual Studio Code, and I just need to add parentheses to call the class. So keyword new is used in TypeScript to create a new instance of the class. And now through the API instance, we can interact with the methods inside of this class. And let's do this. Now I call API. And look, all our methods becomes available. Body, header, parameters, path, and so on. And now we can build our API request that way, technically separating the components of API request into different components, different methods. So I add URL and then provide the parameter of the URL. For example, we want to call, let's do this example, getArticles. So this is our URL. I copy it here, paste it here. And now let's go to the next step. So I can put it to the next line. Then I can script it like this, dot path. The path that we are looking for is articles. So we add articles. Then we need params. And params is the object. So params is limit 10 and offset 0. We can provide params, limit 10 comma offset 0. And the same way, we can add headers, for example. You got the idea, right? So we can provide the headers and then we can provide body and so on. So let me add headers. For the headers, we were using this authorization token. Take this, putting it right here. Authorization of token. Token is not defined. Let's provide some dummy empty string. And for the body, we also can provide the object. And for example, let's take some, I don't know, dummy object. It's not gonna do anything anywhere. So something like this. And we provided the object. Okay, why it's complaining? It's missing syntax error expected. Okay, we're missing one curly brace right here. Okay, now it looks fine. So you see right now how much easier it is to read this test. So we separated URL path parameters and so on. And you can see all the components of your API request. Of course, right now, this is not doing anything. I'm just showing you a high level idea of how you can break down the big API request that looked, for example, like this, something like this, into something easier to read and look nicer, something like this. And just to prove, if I try to run it, it's not gonna work anyway. So you see test is passed. But I can prove that it's already doing some stuff. For example, we can go to our request handler and let's say for the headers, we can print something to the console. Console.log. And let's print this API headers. Okay, and if I run it, here we go. You can see the token is printed to the console. So we successfully can call our instance of the class and the methods inside of this class. So let me remove this. And I guess this is a good stopping point. And let's quickly summarize what we did in this lesson. So we created the request handler. It's gonna be our primary class that will be responsible for managing API request for us. And we created five individual methods breaking down the API request structure into independent components. We were using also known as fluid interface design by providing return this at the end of every method. So we can call those methods like this, chaining one by one using dot notation, URL, path, params, headers, and so on, making our tests significantly more readable. And in the next lessons, we continue developing our request handler, making it much smarter and doing a lot of stuff for us. All right, so see you in the next lesson.