Automatic Authorization | Bondar Academy
Course: Playwright API Testing with TypeScript
Module: Building a Framework
Instructor: Artem Bondar
Lesson Summary
In this lesson, we focus on enhancing our framework by implementing automatic authorization for API requests. This is essential because most API testing involves authenticated requests, making authorization a default behavior in our framework. Key Concepts Authorization Token Usage: The authorization token is required for nearly all API requests. By setting it as a default, we can streamline our code and reduce redundancy. Playwright Configuration: In the Playwright config file, we can set default headers using the extra HTTP headers property. However, this approach limits flexibility as it does not allow for unauthorized requests. Worker Scoped Fixture: We utilize a worker scoped fixture to create the authorization token once per test run. This fixture initializes before any tests are executed, ensuring the token is available for all requests. Implementation Steps Create a worker scoped fixture that generates the authorization token. Pass the generated token to the API fixture and subsequently to the request handler. Implement a method in the request handler to manage the authorization header based on a clearAuth flag . Ensure that if no authorization header is provided, the default token is used, while allowing for the option to clear the token for unauthorized requests. By following these steps, we significantly reduce the amount of code needed for each test, as the authorization header is automatically managed. This optimization leads to faster test execution and less strain on identity management systems. In summary, we have successfully integrated a flexible authorization mechanism into our framework, improving both code maintainability and testing efficiency.
Video Transcript
All right, guys, back to coding. So in this lesson, we will make one more improvement to our framework and we'll make automatic authorization for the framework. All right, and let me show you why do we need this. So here is the smoketest.spec.ts. And if we scroll down to our create and delete article, so look, we use this authorization token pretty much everywhere. So every request has this headers authorization token. So if you think about API automation, this is kind of a default behavior of the framework. So in your real API automation, most of your testing, like 99% of the testing will be with authenticated authorized APIs, with secured APIs. You will not have a free APIs just to test on real projects. So the authorization is a kind of a default behavior that you want to set on your framework so that all API requests will be authorized by default. And just to remove the number of lines of code, it would be nice to configure a framework somewhere to set authorization before you run the test of all your tests. So you don't need to use this headers and passing the token every time for every single request. It just makes code nicer and less maintenance and less time to script your test, okay? This is what we're gonna do. So what options do you have in the playwright? So option number one, how to set the headers, default headers for the entire framework level. In the playwright config file, there is a use block. And inside of use block, there is a property called extra HTTP headers like this. And with this property, you can add any headers that you want. For example, authorization and then whatever value of your token. And if you configure the framework like that, every single API request that you make in the framework will have this authorization header. So from one perspective, quite convenient, but there is a catch. So if you want, for example, to execute API request without this header, you cannot remove it. So you will not be able to clear it and to make, for example, not authorized request. So that's the trade off of this approach. But this place can be useful if you, for example, have API signature. Sometimes this is also a way how to authorize and make sure that your API call is authorized. But for us, we want to keep the framework flexibility that when we need it, we can run unauthorized request. But as a default behavior of the framework, we want to set authorization and be able to remove this authorization if we need it. So this is what we're gonna do in this lesson. I will remove this configuration, but you just know that it exists in case you need it, and we're going back to the test. So how are we gonna handle the authorization? So far, we have this create token function, helper function that can create a token. And let's use a worker scoped fixture to do that. So what is worker scoped fixture? Worker scoped fixture is a fixture that initialized before everything else as a very first step when a worker is created. Remember, I mentioned on the previous lecture that worker is your test executor. And if worker fails, it re-initiate this worker again and then continue running the test. So worker is a perfect place to put your token. So worker will create the token once for all of your tests. And if some of the tests fail, it will create a new token one more time and will continue test execution. So that way, we'll be able to remove the before each and before all hook from the tests completely. And we will create a token once per entire test run or until the test fails. So this is what we're gonna do. So for the worker, we will create worker right here. And I will call the worker scoped fixture as auth token, token. And then syntax for the worker scoped fixture is a little bit weird. So this is the array. And the first argument is the array, your normal async function for the worker, which is like this. First argument is for the arguments of the function, then use, and then the fixture itself. And then we need to add a second argument in this array, which is a scope. So we provide a scope, okay? And scope is worker, like this. Now, this will be a body of our fixture. Now, let's take the function that creates the token for us and copy it inside of the worker fixture. So let's this fixture to do is for us and I create a new constant. Const auth token, create token. This have to be imported from the helpers. All right, what else is missing? We need to put comma over here, okay? And now we need to resolve this auth token option. So we need for the worker fixture, we have to create a new type. We cannot use the same type for the regular fixtures. Worker fixtures should have own type. So I create export type. Worker fixture equals to then auth token of type and return value for the token is a string. And then we need to use await use and use auth token as a return result. All right, so this one is done. And we need to add this type over here to extend next to the test options like this. Okay, all errors were fixed. Now, when this worker fixture is executed, we want to add this fixture as the argument to the API fixture right here. So this fixture will be executed first. It will generate the token and pass this token as the argument to the API fixture. And what we're gonna do with this API token, the value of the API token, let's pass it into request handler inside like this. So and now we need to fix the request handler class to accommodate this auth token. Going back to the request handler here, let's add a new argument to the constructor. Auth token and the type of string and string and also let's set the default value for this token, auth token. So why do we need a default value? Here in the create token, remember we were using just three arguments. So if we will not set any default value for the token, then this constructor will fail. So let me show you that. So if I will remove this default value and go back here, see constructor is failing. There is no way how we can pass the auth token inside of this absolutely independent function. We create this as part of the fixture. So we will create an empty value as a default value and we'll override it later on. Now we need to create a field which I called private default auth token, which will be a string. And then I will assign value this default auth token equals to auth token. That's it. All right, now request handler has the value of the auth token and then we need to use it. So let's talk about the logic that we want to implement over here. So the default authorization should be our default behavior. So if no header is provided, we want to use default authorization token. If no authorization header is provided, we want to use default authorization token. And we also want to have a possibility to clear the authorization token if we want to make unauthorized request. So to implement that, we can create additional helper method that will clear the headers in case we need it. But other than that, by default, we're gonna run authorized request. So to create this method, let's create a Boolean flag, which will be a private, for example, clear auth flag, and which is a Boolean. And we will use this flag to drive the change. And I will create a new method that will be responsible for the value of this flag. So for example, clearAuth. And this will do is this. ClearAuth flag equals to true. So if this flag is true, and then return this, return this. So if this flag is true, it means that we want to clear the authorization. Other than that, use the default authorization. That's the logic we want to implement. Where we get the headers from? Headers we get from this API headers. And now we can create a private method that based on the position of the flag and availability of the headers, can set the default value of the header, okay? So let's create this private method somewhere over here. And I will call this method private getHeaders, getHeaders. So this method will be used to get the right default value of the header. And then the logic. If the clearAuth flag is false, and I will use this method. So if it is not true, it means that we want to set the value of the header. Or do not set any value at all. So what will I do? This, API headers. And inside of the API headers, I would be looking for the header with the key authorization, right? So what would I do with this key? So if the value was provided from the headers method, then just use this value. Otherwise, if there are no value provided, use this default auth token like this. So this logic, and then we need to return this API headers. So by using this flag, we technically modifying how do we want to set the default value for authorization token. So if clearAuth flag was provided, we use just other headers that were provided in the headers method. If the flag was not used, it means if it is false, then we want to check, okay, do we have a header provided from the test request using the headers method, these headers? So if during the test headers method provided authorization header, then this value will be used as a priority value. If no headers were provided, then a default authorization token will be used. And then this method returns updated version of the API headers. And one more thing, we need to add over here a default value for this header, and default value is the false right here. And now we can call this method inside of our action methods, such as get, post, and so on. And instead of using this API headers, we're gonna use this getHeaders. And I will replace this function everywhere. This getHeaders, this getHeaders, this getHeaders, over here, over here, and then delete request right here and right here. Okay, now the method will be responsible for all that stuff. And I guess we can test it, so let's double check. So we have worker's code fixture. This fixture will call the token using the configuration. Token passed as argument into API fixture. This API token value will be passed as argument to the handler. And then in the handler, inside of this method, we will set default auth token. Let's test it. So first of all, going back here, and I remove all of this stuff completely. Checking the list of articles, we have zero articles. All right, so now let's make getArticlesRequest. Making a request, test pass successfully. Now, let's create a dummy article right here. Just creating a dummy article, create. And now we should have 11 articles instead of 10 articles, because we added extra article for this authorized user. So going back, and now if I run the test, this test should fail. Yep, and this test failed because we have 11 articles count. So look, this API call does not have any authorization header, but header was set accordingly at the beginning of the test. All right, so and now let's test, for example, our clear auth. And we need to call it before the action method, for example, params.clearAuth. And let's run this test again. Okay, running test. And now test pass successfully, because we removed authorization header that was set by default. And let's do one more test. I will take this header. I will add it right here. But I will use the different value for this auth token. So I will uncomment this guy temporarily. And let me create a new user account. So setting, setting, settings, API config. I will use this account to create a new session. Going to smoke test, change it user email, and then take the user password, Ctrl C, and Ctrl V. All right, so this auth token, calling this create token feature, will create for me a token for a different user. And I currently have for this user 12 articles created. So I created this precondition before. So if I remove the clear auth, and we'll keep this headers, this auth token should be for a different user, because we will override a default token value. And this test should fail with a different error message. And the error message, look, we have even 13 article. So it was a completely different user. And then I remove the headers, run it again. Now we have first initial account, 11 articles. Then I activate clear auth method, run it, and it works. So everything is working. Our framework right now capable of setting the authorization token by default. And if you need, you can override the value for the token. And this helps to make the cleanup of the test. So look, now I can remove this, I can remove this, I can remove this. And I can remove all those things. It's all not needed anymore. We simplified the scripting by a lot. This, this, this, this, and this. Okay, it's all set. Let me remove this dummy article, delete the article. We can try to run all of our tests all together within this spec file. Smoke test, running the test. And all tests pass successfully using a default configuration. All right, guys, let's quickly summarize what we did in this lesson. I know it was pretty long, but pretty important topic. So we created a worker scoped fixture right here. Worker scoped fixture initiated at the beginning of everything else of all the test execution before the main fixture of API or config fixture. Worker fixture executed once per worker, and this fixture create for us a token. Then we pass this token into the API fixture, and the token value then passed down to the request handler. Inside of the request handler, we have created a simple method that will be responsible for setting up the authorization header value. So if clear auth flag was not provided, that we set the value of the header that was passed in the test. If the value was not provided, just use a default value for the entire framework. Or if we want to clean the header and to create unauthorized request, then we have this little method that just flips the flag. This clear auth, you call the clear auth method, remove authorization and make unauthorized request. So with this little optimization, we removed a lot of code from the test. We don't need to provide authorization header at all. And also, token created just once per test run. And this will reduce pressure from your identity management system and will make your test run faster. All right, guys, and see you in the next lesson.