Links:
pytest --cov=<project_name>
# or
pytest --cov=backend
pytest --cov=contacts --cov-report=term-missing
pip install pytest pytest-bdd
pip install pytest-cov pytest-benchmark flaky pytest-testmon pytest-xdist
[coverage:run]
source = backend
branch = True
omit =
*/.virtualenvs/*
[coverage:report]
precision = 2
exclude_lines =
pragma: no cover
raise NotImplementedError
raise NotImplemented
ignore_errors = True
[coverage:html]
directory = tests/coverage_html_report
[tool:pytest]
asyncio_mode = auto
minversion = 6.0
addopts =
--cov=backend
--cov=tests
--cov-report=html
testpaths =
tests
filterwarnings = ignore::DeprecationWarning
[tool:mypy]
plugins = strawberry.ext.mypy_plugin,pydantic.mypy
follow_imports = silent
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
check_untyped_defs = True
no_implicit_reexport = True
; strict mypy
disallow_untyped_defs = True
[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
warn_untyped_fields = True
https://github.com/nsidnev/fastapi-realworld-example-app/blob/master/pyproject.toml
[tool.pytest.ini_options]
testpaths = "tests"
addopts = '''
--strict-markers
--tb=short
--cov=backend
--cov=tests
--cov-branch
--cov-report=term-missing
--cov-report=html
--no-cov-on-fail
--cov-fail-under=100
'''
filterwarnings = [
"error",
"ignore::UserWarning",
#"ignore::DeprecationWarning",
# note the use of single quote below to denote "raw" strings in TOML
'ignore:function ham\(\) is deprecated:DeprecationWarning',
]
import asyncio
from typing import List
import httpx
import pytest
import pytest_asyncio
from asgi_lifespan import LifespanManager
from motor.motor_asyncio import AsyncIOMotorClient
from bson import ObjectId
from fastapi import status
from chapter6.mongodb.app import app, get_database
from chapter6.mongodb.models import PostDB
motor_client = AsyncIOMotorClient("mongodb://localhost:27017")
database_test = motor_client["db_test"]
def get_test_database():
return database_test
@pytest.fixture(scope="session")
def event_loop():
loop = asyncio.get_event_loop()
yield loop
loop.close()
@pytest_asyncio.fixture
async def test_client():
app.dependency_overrides[get_database] = get_test_database
async with LifespanManager(app):
async with httpx.AsyncClient(app=app, base_url="http://app.io") as test_client:
yield test_client
Notice that we used the autouse and scope arguments of the fixture decorator.
autouse tells pytest to automatically call this fixture even if it's not requested in any test.
In this case, it's convenient because we'll always ensure that the data has been created in the database, without the risk of forgetting to request it in the tests.
scope, allows us to not run this fixture at the beginning of each test.
With the module value, the fixture will create the objects only once, at the beginning of this particular test file.
It helps us keep the test fast because in this case, it doesn't make sense to re-create post before each test function.
# p. 280
@pytest_asyncio.fixture(autouse=True, scope="module")
async def initial_posts():
initial_posts = [
PostDB(title="Post 1", content="Content 1"),
PostDB(title="Post 2", content="Content 2"),
PostDB(title="Post 3", content="Content 3"),
]
await database_test["posts"].insert_many(
[post.dict(by_alias=True) for post in initial_posts])
yield initial_posts
await motor_client.drop_database("db_test")
from datetime import datetime, timedelta
import unittest
from config import Config
from application.scripts.models import MUser
from application import create_app, mondb
import urllib.parse
from flask import url_for
class UserModelCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.app.config['SERVER_NAME'] = 'localhost.localdomain'
self.app.config.from_object('config.Config')
self.app = create_app(TestConfig)
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client(use_cookies=True)
def test_password_hashing(self):
u = MUser(name='susan')
u.set_password('cat')
self.assertFalse(u.check_password('dog'))
self.assertTrue(u.check_password('cat'))
if __name__ == '__main__':
unittest.main(verbosity=2)
Start Locust
locust -f tests_locust.py --host https://abc.com
import time
from locust import HttpUser, task, between, SequentialTaskSet, TaskSet
develop_root = 'https://abc.com'
dev_login = 'https://abc.com'
dev_contact = 'https://abc.com'
admin_email = 'abc@gmail.com'
admin_password = 'abc'
user_email = 'abc@gmail.com'
user_password = 'abc'
# https://docs.locust.io/en/stable/writing-a-locustfile.html#sequentialtaskset-class
class AdminUser(HttpUser):
wait_time = between(5, 8)
@task
class AdminUserTasks(SequentialTaskSet):
# wait_time = between(1, 5)
@task
def first_task(self):
self.client.post(
'/mongo_login',
{
"email": "abc@gmail.ca",
"password": "abc"
}
)
# tasks = [function_task]
@task
def second_task(self):
self.client.get("/account")
@task
def third_task(self):
self.client.get("/contact")
@task
def third_task(self):
self.client.get("/shop")
@task
def logout_task(self):
self.client.get("/logout")
class AdminUser(HttpUser):
wait_time = between(1, 5)
# https://docs.locust.io/en/stable/writing-a-locustfile.html#on-start-and-on-stop-methods
def on_start(self):
self.client.post(
'/mongo_login',
{
"email": "abc@gmail.ca",
"password": "abc"
}
)
@task
def index(self):
self.client.get("/")
# self.client.get("/static/assets.js")
@task
def contact(self):
self.client.get("/contact")
@task
def update_account(self):
self.client.get("/account")
@task
def finished_orders(self):
self.client.get("/orders")
@task
def shop_page(self):
self.client.get("/shop")
@task
def admin_dashboard(self):
self.client.get("/dashboard")
@task
def admin_dashboard_users(self):
self.client.get("/users")
class DevelopBranchLive(HttpUser):
wait_time = between(1, 2)
@task
def home_page(self):
self.client.get('https://www.abc.com')
self.client.get("/hello")
self.client.get("/world")
@task(3)
def view_item(self):
for item_id in range(10):
self.client.get(f"/item?id={item_id}", name="/item")
time.sleep(1)
def on_start(self):
self.client.post("/login", json={"username": "foo", "password": "bar"})
import re
import time
import json
import base64
import os.path
import unittest
from time import sleep
from config import Config
from application import create_app
from flask import url_for
from dotenv import load_dotenv
from werkzeug.security import generate_password_hash, check_password_hash
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from coverage import coverage
basedir = os.path.abspath(os.path.dirname(__file__))
cov = coverage(
branch=True,
omit=[
'test_sheets.py',
'test_two.py',
'~/.virtualenvs/*',
],
)
cov.start()
class TestSeleniumBase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
# driver = webdriver.Chrome(executable_path='/chromedriver')
cls.vars = {}
cls.develop_url = "http://localhost:8080"
cls.develop_url = "http://url.com"
cls.wrong_email = 'abc@gmail.ca'
cls.admin_email = 'abc@gmail.ca'
cls.admin_password = 'abc'
cls.user_email = 'abc@gmail.com'
cls.user_password = 'abc'
cls.sleep_time = 0.7
cls.qty_input = '1'
cls.driver.get(cls.develop_url)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def tearDown(self):
sleep(self.sleep_time)
self.driver.get(self.develop_url + 'logout')
self.driver.find_element(By.ID, "logout-nav-item").click()
logout_a_tag = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, "logout-nav-item")))
logout_a_tag.click()
sleep(1)
self.driver.find_element(By.CSS_SELECTOR, "img").click()
sleep(1)
self.driver.find_element(By.CSS_SELECTOR, "body").click()
class TestSeleniumAdminAcct(TestSeleniumBase):
def setUp(self):
self.driver.get(self.develop_url + 'mongo_login')
self.driver.maximize_window()
self.driver.implicitly_wait(20)#//gives an implicit wait for 20 seconds
self.driver.find_element(By.LINK_TEXT, "Login").click()
self.driver.find_element(By.ID, "login-nav-item").click()
login_a_tag = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.ID, "login-nav-item")))
login_a_tag.click()
sleep(1)
# 4 | click | id=email |
self.driver.find_element(By.ID, "email").click()
sleep(self.sleep_time)
# 5 | type | id=email
self.driver.find_element(By.ID, "email").send_keys(self.wrong_email)
sleep(self.sleep_time)
# 6 | type | id=password |
self.driver.find_element(By.ID, "password").send_keys(self.admin_password)
sleep(self.sleep_time)
# 7 | click | id=submit |
self.driver.find_element(By.ID, "submit").click()
sleep(self.sleep_time)
# 8 | click | id=email |
self.driver.find_element(By.ID, "email").click()
sleep(self.sleep_time)
# 9 | type | id=email
self.driver.find_element(By.ID, "email").send_keys(self.admin_email)
sleep(self.sleep_time)
# 10 | click | id=password |
self.driver.find_element(By.ID, "password").click()
# 11 | type | id=password
self.driver.find_element(By.ID, "password").send_keys(self.admin_password)
sleep(self.sleep_time)
# 12 | click | id=submit |
self.driver.find_element(By.ID, "submit").click()
def test_develop_contact_form(self):
sleep(self.sleep_time)
# 9 | click | linkText=Contact |
self.driver.find_element(By.LINK_TEXT, "Contact").click()
sleep(self.sleep_time)
# 14 | type | id=message | Selenium Contact Page Form from Admin Account
self.driver.find_element(By.ID, "message").send_keys("Selenium Contact Page Form from Admin Account")
sleep(self.sleep_time)
# 15 | click | id=submit |
self.driver.find_element(By.ID, "submit").click()
def test_develop_export_files(self):
sleep(self.sleep_time)
# 9 | click | css=.btn:nth-child(5) > .text-white |
self.driver.find_element(By.CSS_SELECTOR, ".btn:nth-child(5) > .text-white").click()
sleep(self.sleep_time)
# 9 | click | css=.btn:nth-child(4) > .text-white |
self.driver.find_element(By.CSS_SELECTOR, ".btn:nth-child(4) > .text-white").click()
sleep(self.sleep_time)
def test_selenium_develop_checkout(self):
sleep(self.sleep_time)
# 9 | click | linkText=Shop |
self.driver.find_element(By.LINK_TEXT, "Shop").click()
sleep(self.sleep_time)
# 10 | click | id=quantityInput |
self.driver.find_element(By.ID, "quantityInput").click()
sleep(self.sleep_time)
# 11 | type | id=quantityInput | 1
self.driver.find_element(By.ID, "quantityInput").send_keys("1")
sleep(self.sleep_time)
# 12 | click | css=#memberSection .ml-4 |
self.driver.find_element(By.CSS_SELECTOR, "#memberSectionaaa .ml-4").click()
sleep(self.sleep_time)
# 13 | click | id=cartSize |
self.driver.find_element(By.ID, "cartSize").click()
sleep(self.sleep_time)
# 14 | select | id=select_id | label=Comapny 1 Person1 McPers Streetsville 22000
dropdown = self.driver.find_element(By.ID, "select_id")
dropdown.find_element(By.XPATH, "//option[. = 'Comapny 1 Person1 McPers Streetsville 22000']").click()
sleep(self.sleep_time)
# 15 | mouseDownAt | id=select_id | -1244.25,-240
element = self.driver.find_element(By.ID, "select_id")
actions = ActionChains(self.driver)
actions.move_to_element(element).click_and_hold().perform()
sleep(self.sleep_time)
# 16 | mouseMoveAt | id=select_id | -1244.25,-240
element = self.driver.find_element(By.ID, "select_id")
actions = ActionChains(self.driver)
actions.move_to_element(element).perform()
sleep(self.sleep_time)
# 17 | mouseUpAt | id=select_id | -1244.25,-240
element = self.driver.find_element(By.ID, "select_id")
actions = ActionChains(self.driver)
actions.move_to_element(element).release().perform()
sleep(self.sleep_time)
# 18 | click | id=select_id |
self.driver.find_element(By.ID, "select_id").click()
sleep(self.sleep_time)
# 19 | click | css=#complete_checkout > .btn |
self.driver.find_element(By.CSS_SELECTOR, "#complete_checkout > .btn").click()
def test_develop_change_fulfillment_qty(self):
sleep(self.sleep_time)
# 9 | click | name=quantity_number |
# self.driver.find_element(By.NAME, "quantity_number")#.click()
# sleep(self.sleep_time)
# 10 | type | name=quantity_number | 3
self.driver.find_element(By.NAME, "quantity_number").send_keys(self.qty_input)
sleep(self.sleep_time)
# 12 | click | name=down_quantity |
self.driver.find_element(By.NAME, "down_quantity").click()
sleep(self.sleep_time)
# 13 | assertConfirmation | Are you sure you want to update the quantity? |
assert self.driver.switch_to.alert.text == "Are you sure you want to update the quantity?"
sleep(self.sleep_time)
# 14 | webdriverChooseOkOnVisibleConfirmation | |
self.driver.switch_to.alert.accept()