Making a Automated REST API tester in PowerShell , Automating the API Testing

ยท

9 min read

Once upon a time people used to live in the jungle and didn't know how to talk with each other, resulting in improper networking among their peers which in turn led them to not get a chance to work in a M(F)AANG company. To solve this issue, a new way of communicating was introduced known as API(Application program interface). But, to use them they started using API testers like SOAP UI and POSTMAN. Some of the top 1% still used curl, but we are not going to talk about them in this blog. Jokes Apart, Do you know in Powershell we have Invoke-Webrequest, which can be used for sending requests from the command line apart from CURL?

If you're short on time please read from here.

Using Invoke-Webrequest we can stimulate the typical curl behavior in the PowerShell terminal. Also, There is one more method we can use Invoke-RestMethod. So, let's make a get request. I am using ReqRes for the testing

Invoke-WebRequest https://reqres.in/api/users

Running this in PowerShell we can see the response in the terminal, but it's not saved. So, let's save it. Create a text file named res.txt in the same directory and run

Invoke-WebRequest https://reqres.in/api/users | Set-Content -Path res.txt

Here, we are taking the response and pipelining (Read more about it here) the output to Set-Content which is writing the response to our "res.text" file, Path parameter indicates the output file's path. But wait a minute can't we make a file in Powershell itself? Yes, we can So, let's do it. Like Unix/Linux 's touch command to create text files, we have New-Item in Powershell.

New-Item res.txt
Invoke-WebRequest https://reqres.in/api/users | Set-Content -Path res.txt

But, here is one problem the file was already there, right? We don't need to create the file in this case. Let's create the script for this, make sure you save this file with the .ps1 extension. and run by right click on the file and click on run via Powershell or by simply doing ./<FileName>.ps1 in the terminal of the same directory.

if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
Invoke-WebRequest https://reqres.in/api/users | Set-Content -Path res.txt

Here we checked if the file was there using Test-Path. The line Test-Path -Path res.txt -PathType Leaf returns false if the file is not there, So, we are just inverting the conditions and if there is no "res.txt" there, we are creating the file. Now, let us work with the method, it's quite simple we can use the Method parameter for that.

if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
Invoke-WebRequest -Uri https://reqres.in/api/users -Method "GET" | Set-Content -Path res.txt

Now, rather than hard-coding all the values, let's change it to variables.So, we can easily change it later

 if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
$Uri = "https://reqres.in/api/users"
$method = "GET"
$res = Invoke-WebRequest -Uri $uri  -Method $method 
$res | Set-Content -Path res.txt

How about taking the parameters as input from the user? let's implement this

 if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
$Uri = Read-Host "Enter the URI Value"
$method = Read-Host "Enter the METHOD name"
$res = Invoke-WebRequest -Uri $uri  -Method $method 
$res | Set-Content -Path res.txt

Read-Host will take input from the user so our script can take URI and method as input from the user and send the request. Wait a minute we can make the method work like multiple options rather than they have to manually write this. We can create a function for this

function Show-Method {
    Write-Host "Select an option:"
    Write-Host "1. GET"
    Write-Host "2. POST"
    Write-Host "3. PUT"
    Write-Host "4. DELETE"
    $choice = Read-Host "Enter the number of Method"

    switch ($choice) {
        "1" { return "GET" }
        "2" { return "POST" }
        "3" { return "PUT" }
        "4" { return "DELETE" }
        default { return "GET" }
    }

}
 if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
$Uri = Read-Host "Enter the URI Value"
$method = Show-Method
$res = Invoke-WebRequest -Uri $uri  -Method $method 
$res | Set-Content -Path res.txt

Here we are declaring the function and Read-Host is essentially writing to the console then taking the response we are switching it returning the proper strings our method. So, it should look like this

Ok, but for POST or PUT requests we need to send some json via body. We will do this part by part. At first, we will take the input file location but we have to remember it's optional.

$body = Read-Host "Enter file location of json file for body"

#body
if ($body.length -gt 0) {
    # Read the content of the file
    $bodyData = Get-Content -Path $body -Raw
}

Here we are taking file location in the body. We are checking to if there's input here one point we use > as -gt in this language. If the location id there we are Using Get-Content for the json and storing in bodyData. We also have to change the headers of the file to let our server know that we are sending it a JSON file and trigger our Invoke-WebRequest.

$body = Read-Host "Enter file location of json file for body"

#body
if ($body.length -gt 0) {
    # Read the content of the file
    $bodyData = Get-Content -Path $body -Raw
      #  change headers if needed
    $headers = @{
        "Content-Type" = "application/json"
    }
    $response = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $bodyData
}

So, Compiling all the code our now script is

function Show-Method {
    Write-Host "Select an option:"
    Write-Host "1. GET"
    Write-Host "2. POST"
    Write-Host "3. PUT"
    Write-Host "4. DELETE"
    $choice = Read-Host "Enter the number of Method"

    switch ($choice) {
        "1" { return "GET" }
        "2" { return "POST" }
        "3" { return "PUT" }
        "4" { return "DELETE" }
        default { return "GET" }
    }

}
 if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
$Uri = Read-Host "Enter the URI Value"
$method = Show-Method
$body = Read-Host "Enter file location of json file for body"

#body
if ($body.length -gt 0) {
    # Read the content of the file
    $bodyData = Get-Content -Path $body -Raw
      #  change headers if needed
    $headers = @{
        "Content-Type" = "application/json"
    }
    $response = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $bodyData
}else{
$res = Invoke-WebRequest -Uri $uri  -Method $method 
}
$res | Set-Content -Path res.txt
# open the response in notepad
notepad res.txt

Let's Automate

As our rest API tester is ready let us automate the scripts to hit the api multiple time. Foe this first let us wrap all the code in a function

function Test-Api {
function Show-Method {
    Write-Host "Select an option:"
    Write-Host "1. GET"
    Write-Host "2. POST"
    Write-Host "3. PUT"
    Write-Host "4. DELETE"
    $choice = Read-Host "Enter the number of Method"

    switch ($choice) {
        "1" { return "GET" }
        "2" { return "POST" }
        "3" { return "PUT" }
        "4" { return "DELETE" }
        default { return "GET" }
    }

}
 if (!(Test-Path -Path res.txt -PathType Leaf)) {
    New-Item res.txt
}
$Uri = Read-Host "Enter the URI Value"
$method = Show-Method
$body = Read-Host "Enter file location of json file for body"

#body
if ($body.length -gt 0) {
    # Read the content of the file
    $bodyData = Get-Content -Path $body -Raw
      #  change headers if needed
    $headers = @{
        "Content-Type" = "application/json"
    }
    $response = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $bodyData
}else{
$res = Invoke-WebRequest -Uri $uri  -Method $method 
}
$res | Set-Content -Path res.txt
}
# open the response in notepad
notepad res.txt

Now, we don't want to take input every time rather than pass some input from the file So, we will delete the Read-Host and will pass parameters.Also, we need to change the body type too

function Test-Api {
    param(
        [string]$Uri,
        [string]$method,
        [string]$body
    )

    if (!(Test-Path -Path res.txt -PathType Leaf)) {
        New-Item res.txt
    }
    #body
    if ($body.length -gt 0) {
        #  change headers if needed
        $headers = @{
            "Content-Type" = "application/json"
        }
        $res = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $body
    }
    else {
        $res = Invoke-WebRequest -Uri $uri  -Method $method 
    }
}

Here We will change how we write the response rather than writing the file every time as new, we will just append the Response now.

function Test-Api {
    param(
        [string]$Uri,
        [string]$method,
        [string]$body
    )
    function Show-Method {
        Write-Host "Select an option:"
        Write-Host "1. GET"
        Write-Host "2. POST"
        Write-Host "3. PUT"
        Write-Host "4. DELETE"
        $choice = Read-Host "Enter the number of Method"

        switch ($choice) {
            "1" { return "GET" }
            "2" { return "POST" }
            "3" { return "PUT" }
            "4" { return "DELETE" }
            default { return "GET" }
        }

    }
    if (!(Test-Path -Path res.txt -PathType Leaf)) {
        New-Item res.txt
    }

    #body
    if ($body.length -gt 0) {
        #  change headers if needed
        $headers = @{
            "Content-Type" = "application/json"
        }
        $res = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $body
    }
    else {
        $res = Invoke-WebRequest -Uri $uri  -Method $method 
    }
    Add-Content -Path res.txt "request:"
    Add-Content -Path .\res.txt $body
    Add-Content -Path res.txt "response:"
    Add-Content -Path res.txt $res
}

Let's make a req.json file with a response array


[
    {
        "name": "morpheus name",
        "job": "leader bro4"
    },{
        "name": "morpheus name",
        "job": "leader bro2"
    },{

        "name": "morpheus name",
        "job": "leader bro1"
    },{

        "name": "morpheus name",
        "job": "leader bro5"
    }
]

and at last, we are going to read the JSON from the file.

function Test-Api {
    param(
        [string]$Uri,
        [string]$method,
        [string]$body
    )
    if (!(Test-Path -Path res.txt -PathType Leaf)) {
        New-Item res.txt
    }

    #body
    if ($body.length -gt 0) {
        $headers = @{
            "Content-Type" = "application/json"
        }
        $res = Invoke-WebRequest -Uri $uri -Method $method -Headers $headers -Body $body
    }
    else {
        $res = Invoke-WebRequest -Uri $uri  -Method $method 
    }
    Add-Content -Path res.txt "request:"
    Add-Content -Path .\res.txt $body
    Add-Content -Path res.txt "response:"
    Add-Content -Path res.txt $res
}

# automate the flow

$reqs =  Get-Content -Path .\req.json -Raw | ConvertFrom-Json

forEach($i in $reqs){
    $req = $i | ConvertTo-Json
    Test-Api -Uri https://reqres.in/api/users -method Post -body $req
}

notepad res.txt

Here at last as it is a PowerShell array we needed to convert it from JSON(using ConvertFrom-Json) to iterate through it but also as we need to send JSON as a request so we again have to convert it to JSON(ConvertTo-Json).

last notepad res.txt will automatically open the file in Notepad (Optional Step). We can use this script and tweak this according to our needs to even a lot to automate our API Testing with minimal configuration. You can also download the file in my GitHub gist.

Before you go, Do you have any recommendations/advice for me? Have any project/product, I can contribute to? We can connect via LinkedIn / Email and have a Conversation. ๐Ÿ‘‹๐Ÿ‘‹

ย