├── .gitignore ├── Dockerfile ├── README.md ├── features ├── environment.py ├── pages │ ├── base_page.py │ ├── login_page.py │ └── main_page.py ├── search.feature ├── steps │ └── search.py └── support │ └── users.py ├── gif └── img └── behave.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | c 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | MAINTAINER Mesut Güneş 4 | 5 | # Set timezone 6 | RUN echo "US/Eastern" > /etc/timezone && dpkg-reconfigure --frontend noninteractive tzdata 7 | 8 | RUN apt-get update -y && \ 9 | apt-get install -y unzip xvfb \ 10 | qt5-default libqt5webkit5-dev \ 11 | gstreamer1.0-plugins-base \ 12 | gstreamer1.0-tools gstreamer1.0-x \ 13 | freetds-dev \ 14 | libnss3 libxi6 libgconf-2-4 \ 15 | xvfb 16 | 17 | # install chrome 18 | RUN apt-get update -y && \ 19 | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \ 20 | dpkg -i google-chrome-stable_current_amd64.deb; apt-get -fy install 21 | 22 | # install chromedriver and place it ib path 23 | RUN wget https://chromedriver.storage.googleapis.com/2.42/chromedriver_linux64.zip && \ 24 | unzip chromedriver_linux64.zip && \ 25 | mv chromedriver /usr/local/bin/ 26 | 27 | # install Python packages 28 | RUN pip3 install selenium 29 | RUN pip3 install behave 30 | RUN pip3 install allure-behave 31 | RUN pip3 install pyquery 32 | 33 | 34 | # create seperate folder for project 35 | RUN mkdir /project 36 | WORKDIR /project 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Selenium Behave Page-Object-Model(POM) Docker 2 | Run your Selenium BDD (Behaviour Driven Development) test cases in Docker. Python, Selenium, Behave, Chrome, Docker. Page object mode (POM) is applied in the project. 3 | 4 | # Demo 5 | **`Behave`** is Python-Selenium equivalent of **`Cucumber`** Ruby-Capybara. You can run your BBD test cases in the same way with some little differences. See the demo which runs Behave in Docker: 6 | ![Behave demo GIF](img/behave.gif) 7 | -------------------------------------------------------------------------------- /features/environment.py: -------------------------------------------------------------------------------- 1 | from behave import * 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from selenium.webdriver.common.by import By 6 | from selenium.webdriver.chrome.options import Options 7 | from selenium.webdriver.common.keys import Keys 8 | 9 | 10 | def before_scenario(context, scenario): 11 | options = Options() 12 | options.add_argument("--headless") # Runs Chrome in headless mode. 13 | options.add_argument('--no-sandbox') # # Bypass OS security model 14 | options.add_argument('start-maximized') 15 | options.add_argument('disable-infobars') 16 | options.add_argument("--disable-extensions") 17 | options.add_argument('--disable-gpu') 18 | 19 | context.browser = webdriver.Chrome(chrome_options=options) 20 | 21 | def after_scenario(context, scenario): 22 | context.browser.quit() -------------------------------------------------------------------------------- /features/pages/base_page.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.common.by import By 3 | from selenium.webdriver.common.action_chains import ActionChains 4 | from selenium.webdriver.chrome.options import Options 5 | 6 | # this Base class is serving basic attributes for every single page inherited from Page class 7 | class BasePage(object): 8 | 9 | def __init__(self, driver, base_url='http://www.amazon.com/'): 10 | self.base_url = base_url 11 | self.driver = driver 12 | self.timeout = 30 13 | 14 | def find_element(self, *locator): 15 | return self.driver.find_element(*locator) 16 | 17 | def open(self,url): 18 | url = self.base_url + url 19 | self.driver.get(url) 20 | 21 | def get_title(self): 22 | return self.driver.title 23 | 24 | def get_url(self): 25 | return self.driver.current_url 26 | 27 | def hover(self, *locator): 28 | element = self.find_element(*locator) 29 | hover = ActionChains(self.driver).move_to_element(element) 30 | hover.perform() 31 | 32 | -------------------------------------------------------------------------------- /features/pages/login_page.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.common.keys import Keys 3 | from base import Page 4 | from locators import * 5 | import users 6 | from selenium.webdriver.support.ui import WebDriverWait 7 | 8 | 9 | class LoginPage(Page): 10 | def __init__(self, driver): 11 | self.locator = LoginPageLocatars 12 | super(LoginPage, self).__init__(driver) # Python2 version 13 | 14 | def enter_email(self, user): 15 | self.find_element(*self.locator.EMAIL).send_keys(users.get_user(user)["email"]) 16 | 17 | def enter_password(self, user): 18 | self.find_element(*self.locator.PASSWORD).send_keys(users.get_user(user)["password"]) 19 | 20 | def click_login_button(self): 21 | self.find_element(*self.locator.SUBMIT).click() 22 | 23 | def login(self, user): 24 | self.enter_email(user) 25 | self.enter_password(user) 26 | self.click_login_button() 27 | 28 | def login_with_valid_user(self, user): 29 | self.login(user) 30 | return HomePage(self.driver) 31 | 32 | def login_with_in_valid_user(self, user): 33 | self.login(user) 34 | return self.find_element(*self.locator.ERROR_MESSAGE).text -------------------------------------------------------------------------------- /features/pages/main_page.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from selenium.webdriver.common.keys import Keys 3 | from pages.base_page import BasePage 4 | from support.users import users 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.common.by import By 7 | 8 | 9 | 10 | # Page opjects are written in this module. 11 | # Depends on the page functionality we can have more functions for new classes 12 | 13 | 14 | class MainPage(BasePage): 15 | # Web elements on Main Page 16 | LOGO = (By.ID, 'nav-logo') 17 | ACCOUNT = (By.ID, 'nav-link-yourAccount') 18 | SIGNUP = (By.CSS_SELECTOR, '#nav-flyout-ya-newCust > a') 19 | LOGIN = (By.CSS_SELECTOR, '#nav-flyout-ya-signin > a') 20 | SEARCH = (By.ID, 'twotabsearchtextbox') 21 | SEARCH_LIST = (By.ID, 's-results-list-atf') 22 | 23 | def __init__(self, driver): 24 | super().__init__(driver) # Python3 version 25 | 26 | def check_page_loaded(self): 27 | return True if self.find_element(*self.LOGO) else False 28 | 29 | def search_item(self, item): 30 | self.find_element(*self.SEARCH).send_keys(item) 31 | self.find_element(*self.SEARCH).send_keys(Keys.ENTER) 32 | 33 | def get_search_result(self): 34 | return self.find_element(*self.SEARCH_LIST).text 35 | 36 | def click_sign_up_button(self): 37 | self.hover(*self.ACCOUNT) 38 | self.find_element(*self.SIGNUP).click() 39 | return SignUpPage(self.driver) 40 | 41 | def click_sign_in_button(self): 42 | self.hover(*self.ACCOUNT) 43 | self.find_element(*self.LOGIN).click() 44 | return LoginPage(self.driver) -------------------------------------------------------------------------------- /features/search.feature: -------------------------------------------------------------------------------- 1 | Feature: Search stuff on the main page of Amazon 2 | Background: go to main page of Amazon 3 | Given I open the main page 4 | 5 | @search @smoke 6 | Scenario: Searching on the main page 7 | When I search for "LG V30" 8 | Then I should see "LG V30" in search result 9 | -------------------------------------------------------------------------------- /features/steps/search.py: -------------------------------------------------------------------------------- 1 | from behave import * 2 | 3 | import unittest 4 | from selenium import webdriver 5 | from pages.main_page import MainPage 6 | 7 | 8 | 9 | @given(u'I open the main page') 10 | def step_impl(context): 11 | global page 12 | page = MainPage(context.browser) 13 | page.open('/') 14 | page.check_page_loaded() 15 | 16 | 17 | @when(u'I search for "{item}"') 18 | def step_impl(context, item): 19 | page.search_item(item) 20 | 21 | 22 | @then(u'I should see "{item}" in search result') 23 | def step_impl(context, item): 24 | assert item in page.get_search_result() 25 | -------------------------------------------------------------------------------- /features/support/users.py: -------------------------------------------------------------------------------- 1 | from operator import itemgetter 2 | 3 | # we can store test data in this module like users 4 | 5 | users = [ 6 | {"name": "invalid_user", "email": "invalidUser@test.com", "password": "qwert1235"}, 7 | {"name": "valid_user", "email": "validUser@yahoo.com", "password": "ValidPassword"}, 8 | {"name": "Staff2", "email": "staff@test.com", "password": "qwert1235"}, 9 | {"name": "Admin0", "email": "admin@test.com", "password": "qwert1234"}, 10 | {"name": "Admin1", "email": "admin@test.com", "password": "qwert1234"}, 11 | {"name": "Admin2", "email": "admin@test.com", "password": "qwert1234"}, 12 | ] 13 | 14 | def get_user(name): 15 | try: 16 | return (user for user in users if user["name"] == name).next() 17 | except: 18 | print("\n User %s is not defined, enter a valid user.\n" %name) 19 | -------------------------------------------------------------------------------- /gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunesmes/python-selenium-behave-page-object-docker/dd6346f8654b89a320223b947b34fa5d86408c8c/gif -------------------------------------------------------------------------------- /img/behave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunesmes/python-selenium-behave-page-object-docker/dd6346f8654b89a320223b947b34fa5d86408c8c/img/behave.gif --------------------------------------------------------------------------------