-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #64 from Volatar/api
Merge API branch
- Loading branch information
Showing
7 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# API Testing Tutorial | ||
## What Is API Testing? | ||
API Testing, unlike most tests, focuses specifically on Application Programming Interfaces (APIs). | ||
APIs serve as a link between different software applications, facilitating communication. | ||
They establish rules and tools for seamless data exchange. | ||
API Testing involves creating and executing tests that validate various aspects of APIs to ensure their proper functionality. | ||
This includes observing and recording status codes, automated validation of API endpoints, and making seamless HTTP requests. | ||
API Testing is crucial for ensuring not only a functional API but also a reliable software system. | ||
|
||
## What Will We Use to Test? | ||
For this tutorial, we will use Pytest as our testing tool. | ||
Pytest is an efficient framework that simplifies API Testing. | ||
To get started, make sure to install the required packages by entering `pip install pytest requests` and `pip install Flask Flask-RESTful` into your terminal. | ||
When creating your test file, follow the Pytest convention by naming it with a `test_` prefix, and that any methods within are also begun with `test_`. | ||
Ensure that the code file includes `import requests` and `import pytest` before generating any tests. | ||
|
||
## Additional Information | ||
Note that this tutorial does not cover all available options for API testing, and not every type of test is included as an example. | ||
Feel free to explore the provided code for additional insights and information that may not be explicitly covered in this tutorial. | ||
Good luck! | ||
|
||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# API Test Examples | ||
|
||
When performing API testing, it is always crucial to include imports for requests. | ||
Importing requests allows the following test examples to work, and thereby is defining GET, PUT, DELETE, and POST test attempts. | ||
In this test file, we have a base URL to work with as well, which is also attached toward the top of the file. | ||
If you were using someone else's server to test your API calls against, a variable like this would be where you would put that URL. | ||
```py | ||
import requests | ||
|
||
base = "http://127.0.0.1:5000/" | ||
``` | ||
## Example 1: GET | ||
```py | ||
def test_get(): | ||
response = requests.get(base + "iceCream/4") | ||
assert response.json() == {"4": "Cotton Candy"} | ||
assert response.status_code == 200 | ||
``` | ||
This test makes an HTTP GET request to a constructed endpoint. | ||
`iceCream/4` is appended to the base URL and two assert statements are made. | ||
Once the GET request is made, the server response is stored in the `response` variable. | ||
The first assertion statement checks to see if the JSON content of `response` is equal to `{"4": "Cotton Candy"}`, and returns True if it is equal. | ||
It returns False if it is not equal, so that an exception will be raised. | ||
The second assertion statement for this and all tests presented asserts the HTTP status code, and passes if the statement returns True. | ||
Otherwise, an exception is raised and indicates test failure. | ||
Do note that both assertions have to pass for the test to indicate as a success. | ||
The terminal output for this test is shown below: | ||
|
||
 | ||
|
||
## Example 2: DELETE | ||
```py | ||
def test_delete(): | ||
response = requests.delete(base + "iceCream/5") | ||
assert response.json() == {"message": "Item is deleted from the menu"} | ||
assert response.status_code == 200 | ||
``` | ||
This test makes an HTTP DELETE request to an endpoint made by appending `iceCream/5` to the base URL, then stores the server response in the `response` variable. | ||
Two assert statements are made to verify a successful deletion. | ||
One checks to ensure the content reflects the deletion and the other asserts matching status code. | ||
Terminal output displayed below: | ||
|
||
 | ||
|
||
Within the test file, every successful test has an alternative test to ensure a failed GET, PUT, DELETE, or POST test is validated. | ||
The next and last test example will be for an intended failure. | ||
|
||
## Example 3: DELETE (Intended Failure) | ||
```py | ||
def test_delete_no_id(): | ||
response = requests.delete(base + "iceCream/5") | ||
assert response.json() == {"message": "ID is invalid"} | ||
assert response.status_code != 200 | ||
``` | ||
This test is performing the same actions as the test within Example 2, though the result is meant to be very different. | ||
When the assert statements are made, the first asserts for an error concerning a lack of valid ID, the second asserts that the value of `status_code` is not equal to the intended successful status code. | ||
Should the test *succeed* in the DELETE request, an exception is thrown and the test fails. | ||
Terminal output is displayed below: | ||
|
||
 | ||
|
||
With these examples, you should have a better understanding of API Testing. | ||
As mentioned prior; GET, PUT, DELETE, and POST all have alternate intended failure tests. | ||
More examples are presented within the `test.py` file, which you may view at your own will. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# API Testing With Postman | ||
## Introduction to Postman | ||
Postman is an API platform that provides tools for building, testing, and using APIs. | ||
It can make HTTP requests, save environments for later, and convert API code into languages like Python and JavaScript. | ||
All of this functionality is handled by a simple and easy-to-use user interface. | ||
As a result, Postman is used by millions of API developers every year for all their API needs. | ||
|
||
## How to Set up Postman | ||
1. First, you must download Postman to be able to use it. | ||
Whilst there is a variety of Postman products to use, we are just going to download the free version. | ||
[Click here for the link](https://www.postman.com/downloads/) | ||
|
||
 | ||
|
||
2. After downloading Postman from that link, you must now install it. | ||
Simply click on the setup file you downloaded, and it should install everything. | ||
3. You will be presented with a menu that will ask you to sign up or create an account. | ||
You can create an account, or you can select an option towards the bottom of the window to skip this process. | ||
It should be noted that if you do not have an account associated with your Postman application, you will be limited in certain actions you can do with Postman and your UI will be different. | ||
This lab assumes you have created an account. | ||
|
||
## How to Test With Postman | ||
To keep things simple and to work with the basic API template we have used throughout this lab, we are going to focus on using HTTP requests with Postman. | ||
If you did not create an account with Postman, this will be possible but will be slightly different. | ||
The requests should be the same, but you will not be able to create collections and workspaces or take advantage of templates. | ||
You should however still be able to follow along. | ||
|
||
### Step-By-Step | ||
1. You should be in a default workspace, you can either create a new one or use the current one. | ||
However, you will want to create a new collection, in this case, a REST API basics template is perfect, or you can create your own with a blank template. | ||
|
||
 | ||
|
||
If you use the blank template you will have to create your own requests. | ||
In this example, it is recommended you use the REST API basics template. | ||
2. You should now be presented with, or create yourself, four requests: a GET request, a POST request, a PUT request, and finally a DEL request. | ||
Each of the API request methods should be familiar, with a GET retrieving data, a POST adding data, a PUT updating data, and a DEL deleting data in the API. | ||
Let's start with the simplest request, the GET request. | ||
3. To modify the GET request, simply select it. | ||
First, you will want to provide the request with the API's URL. | ||
For our tutorial, you can use our simple API, simply by pasting `http://127.0.0.1:5000/iceCream`. | ||
|
||
 | ||
|
||
You will also want to actually retrieve something specific. | ||
In this case, let us retrieve the 4th flavor on the menu. | ||
Add `/4` to the end of the URL and click Send. | ||
|
||
 | ||
|
||
You should receive a dictionary containing the flavor "Cookies and Creme" unless the API was recently modified. | ||
|
||
 | ||
|
||
4. Now let us move on to something a little more complicated, the POST method. | ||
First, you will want to select the POST method in your collection and then enter the same URL from the GET method, except instead of `/4` use `/6`. | ||
To keep this tutorial simple and to work similarly to the API requests in Python, we will then select the Body tab below the URL. | ||
From there you might want to select the form-data bubble to ensure that your parameters are clear and make sense, though any format you feel comfortable with should work. | ||
On form-data, you want to **double-click** on the sections in the table below the first row, this will allow you to select the key and its value. | ||
|
||
 | ||
|
||
In this case, the key should be `name` and the value can be whatever flavor you want. | ||
After entering the key and its value into the body, you can now click the Send button, and your request should process and return the flavor you entered (We used Rocky Road as an Example). | ||
5. Next, use the PUT request method. | ||
This method will follow the same details as mentioned in step #4. | ||
The main exception is that you should be updating the current menu item to another flavor. | ||
Therefore, under the body tab, you should change the value to `Butter Pecan`. | ||
Once you send this request it will return the new flavor, replacing the flavor you came up with before. | ||
6. Finally, let's get rid of the 6th flavor, with the DEL request method. | ||
In this case, you will not have to add anything under the body tab, just keep the same URL from steps #4 and #5. | ||
Once you have sent the request, you will be returned a message saying `"Item is deleted from the menu"`. | ||
|
||
This should give a basic idea of how to run HTTP requests and interact with our simple API lab template. | ||
Feel free to experiment with the requests presented and interact with the API. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from flask import Flask, request | ||
from flask_restful import Api, Resource, abort | ||
|
||
# Create app and api | ||
app = Flask(__name__) | ||
api = Api(app) | ||
|
||
# Ice cream menu dictionary | ||
iceCream = {1: "Vanilla", 2: "Chocolate", 3: "Strawberry", 4: "Cotton Candy", 5: "Mint Chocolate Chip"} | ||
|
||
|
||
# Validate whether ice cream already exists | ||
def abort_if_id_exists(ice_cream_id): | ||
if ice_cream_id in iceCream: | ||
abort(409, message="ID already exists") | ||
|
||
|
||
# Validate whether ice cream does not exist | ||
def abort_if_id_does_not_exists(ice_cream_id): | ||
if ice_cream_id not in iceCream: | ||
abort(404, message="ID is invalid") | ||
|
||
|
||
# Api resource for ice cream menu | ||
class IceCream(Resource): | ||
|
||
def get(self, ice_cream_id): | ||
abort_if_id_does_not_exists(ice_cream_id) | ||
return {ice_cream_id: iceCream[ice_cream_id]}, 200 | ||
|
||
def post(self, ice_cream_id): | ||
abort_if_id_exists(ice_cream_id) | ||
iceCream[ice_cream_id] = request.form['name'] | ||
return {ice_cream_id: iceCream[ice_cream_id]}, 201 | ||
|
||
def put(self, ice_cream_id): | ||
abort_if_id_does_not_exists(ice_cream_id) | ||
iceCream[ice_cream_id] = request.form['name'] | ||
return {ice_cream_id: iceCream[ice_cream_id]}, 201 | ||
|
||
def delete(self, ice_cream_id): | ||
abort_if_id_does_not_exists(ice_cream_id) | ||
del iceCream[ice_cream_id] | ||
return {'message': 'Item is deleted from the menu'}, 200 | ||
|
||
|
||
# Add resource to api | ||
api.add_resource(IceCream, "/iceCream/<int:ice_cream_id>") | ||
|
||
# Run main | ||
if __name__ == "__main__": | ||
app.run(debug=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import requests | ||
|
||
# Base url | ||
base = "http://127.0.0.1:5000/" | ||
|
||
|
||
def test_get(): | ||
response = requests.get(base + "iceCream/4") | ||
assert response.json() == {"4": "Cotton Candy"} | ||
assert response.status_code == 200 | ||
|
||
|
||
def test_get_no_id(): | ||
response = requests.get(base + "iceCream/6") | ||
assert response.json() == {"message": "ID is invalid"} | ||
assert response.status_code != 200 | ||
|
||
|
||
def test_put(): | ||
response = requests.put(base + "iceCream/4", data={"name": "Cookies and Creme"}) | ||
assert response.json() == {"4": "Cookies and Creme"} | ||
assert response.status_code == 201 | ||
|
||
|
||
def test_get_again(): | ||
response = requests.get(base + "iceCream/4") | ||
assert response.json() == {"4": "Cookies and Creme"} | ||
assert response.status_code == 200 | ||
|
||
|
||
def test_put_no_id(): | ||
response = requests.put(base + "iceCream/6", data={"name": "Cookies and Creme"}) | ||
assert response.json() == {"message": "ID is invalid"} | ||
assert response.status_code != 201 | ||
|
||
|
||
def test_delete(): | ||
response = requests.delete(base + "iceCream/5") | ||
assert response.json() == {"message": "Item is deleted from the menu"} | ||
assert response.status_code == 200 | ||
|
||
|
||
def test_delete_no_id(): | ||
response = requests.delete(base + "iceCream/5") | ||
assert response.json() == {"message": "ID is invalid"} | ||
assert response.status_code != 200 | ||
|
||
|
||
def test_post(): | ||
response = requests.post(base + "iceCream/5", data={"name": "Chocolate Chip Cookie Dough"}) | ||
assert response.json() == {"5": "Chocolate Chip Cookie Dough"} | ||
assert response.status_code == 201 | ||
|
||
|
||
def test_post_existing_id(): | ||
response = requests.post(base + "iceCream/3", data={"name": "Chocolate Chip Cookie Dough"}) | ||
assert response.json() == {"message": "ID already exists"} | ||
assert response.status_code != 201 |
11 changes: 11 additions & 0 deletions
11
...um Lab/Lab Documentation/requirements.txt → requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters