Added feature to be able to connect with selenium grid
This commit is contained in:
parent
a802bdddee
commit
b37f31b01c
13
README.md
13
README.md
|
@ -17,7 +17,8 @@ Features
|
|||
1. Android emulator
|
||||
2. noVNC
|
||||
3. Appium server
|
||||
4. Browser application for mobile website testing
|
||||
4. Able to connect to selenium grid
|
||||
5. Browser application for mobile website testing
|
||||
- Chrome version 55 (for x86 and armeabi)
|
||||
- Firefox version 51 (for x86 and armeabi)
|
||||
|
||||
|
@ -29,14 +30,20 @@ Quick Start
|
|||
2. Run docker-appium with command:
|
||||
|
||||
```bash
|
||||
docker run -d -p 6080:6080 -p 4723:4723 -v <path_of_apk_that_want_to_be_tested>:/target_apk -e ANDROID_VERSION=<target_android_version> -e EMULATOR_TYPE=<emulator_type> --name appium-container butomo1989/docker-appium
|
||||
docker run -d -p 6080:6080 -p 4723:4723 -v <path_of_apk_that_want_to_be_tested>:/target_apk -e ANDROID_VERSION=<target_android_version> -e EMULATOR_TYPE=<emulator_type> -e CONNECT_TO_GRID=<True/False> --name appium-container butomo1989/docker-appium
|
||||
```
|
||||
|
||||
An Example:
|
||||
|
||||
```bash
|
||||
docker run -d -p 6080:6080 -p 4723:4723 -v $PWD/example/sample_apk:/target_apk -e ANDROID_VERSION=4.2.2 -e EMULATOR_TYPE=armeabi --name appium-container butomo1989/docker-appium
|
||||
docker run -d -p 6080:6080 -p 4723:4723 -v $PWD/example/sample_apk:/target_apk -e ANDROID_VERSION=4.2.2 -e EMULATOR_TYPE=armeabi -e CONNECT_TO_GRID=False --name appium-container butomo1989/docker-appium
|
||||
```
|
||||
**Optional arguments for CONNECT\_TO\_GRID=True**
|
||||
|
||||
-e APPIUM_HOST="<host_ip_address>": if appium is running under different host. default value: 127.0.0.1
|
||||
-e APPIUM_PORT=<port_number>: if appium is running under different port. default port: 4723
|
||||
-e SELENIUM_HOST="<host_ip_address>": if selenium hub is running under different host. default value: 127.0.0.1
|
||||
-e SELENIUM_PORT=<port_number>: if selenium hub is running under different port. default port: 4444
|
||||
|
||||
**Note: use flag *--privileged* and *EMULATOR_TYPE=x86* for ubuntu OS to make emulator faster**
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import os
|
||||
|
||||
WORKDIR = os.path.dirname(__file__)
|
||||
CONFIG_FILE = os.path.join(WORKDIR, 'nodeconfig.json')
|
50
service/appium.py
Normal file
50
service/appium.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import json
|
||||
|
||||
|
||||
def create_node_config(config_file, emulator_name, android_version, appium_host, appium_port,
|
||||
selenium_host, selenium_port):
|
||||
"""
|
||||
Create custom node config file in json format to be able to connect with selenium server.
|
||||
|
||||
:param config_file: config file
|
||||
:type config_file: str
|
||||
:param emulator_name: emulator name
|
||||
:type emulator_name: str
|
||||
:param android_version: android version of android emulator
|
||||
:type android_version: str
|
||||
:param appium_host: host where appium server is running
|
||||
:type appium_host: str
|
||||
:param appium_port: port number where where appium server is running
|
||||
:type appium_port: int
|
||||
:param selenium_host: host where selenium server is running
|
||||
:type selenium_host: str
|
||||
:param selenium_port: port number where selenium server is running
|
||||
:type selenium_port: int
|
||||
|
||||
"""
|
||||
config = {
|
||||
'capabilities': [
|
||||
{
|
||||
'platform': 'Android',
|
||||
'platformName': 'Android',
|
||||
'version': android_version,
|
||||
'browserName': emulator_name,
|
||||
'maxInstances': 1,
|
||||
}
|
||||
],
|
||||
'configuration': {
|
||||
'cleanUpCycle': 2000,
|
||||
'timeout': 30000,
|
||||
'proxy': 'org.openqa.grid.selenium.proxy.DefaultRemoteProxy',
|
||||
'url': 'http://{host}:{port}/wd/hub'.format(host=appium_host, port=appium_port),
|
||||
'host': appium_host,
|
||||
'port': appium_port,
|
||||
'maxSession': 6,
|
||||
'register': True,
|
||||
'registerCycle': 5000,
|
||||
'hubHost': selenium_host,
|
||||
'hubPort': selenium_port
|
||||
}
|
||||
}
|
||||
with open(config_file, 'w') as cf:
|
||||
cf.write(json.dumps(config))
|
|
@ -3,8 +3,12 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
|
||||
import appium
|
||||
|
||||
from service import CONFIG_FILE
|
||||
|
||||
logging.basicConfig()
|
||||
logger = logging.getLogger('android_appium')
|
||||
logger = logging.getLogger('main')
|
||||
|
||||
# not using enum because need to install pip that will make docker image size bigger
|
||||
TYPE_ARMEABI = 'armeabi'
|
||||
|
@ -18,7 +22,8 @@ def run():
|
|||
"""
|
||||
# Get android version package
|
||||
android_version = os.getenv('ANDROID_VERSION', '4.2.2')
|
||||
os.environ['emulator_name'] = 'emulator_{version}'.format(version=android_version)
|
||||
emulator_name = 'emulator_{version}'.format(version=android_version)
|
||||
os.environ['EMULATOR_NAME'] = emulator_name
|
||||
|
||||
# Get emulator type
|
||||
types = [TYPE_ARMEABI, TYPE_X86]
|
||||
|
@ -33,16 +38,35 @@ def run():
|
|||
subprocess.check_call('Xvfb ${DISPLAY} -screen ${SCREEN} ${SCREEN_WIDTH}x${SCREEN_HEIGHT}x${SCREEN_DEPTH} & '
|
||||
'sleep ${TIMEOUT}', shell=True)
|
||||
|
||||
# Start noVNC, installation of android packages, emulator creation and appium
|
||||
# Start noVNC
|
||||
vnc_cmd = 'openbox-session & x11vnc -display ${DISPLAY} -nopw -ncache 10 -forever & ' \
|
||||
'./noVNC/utils/launch.sh --vnc localhost:${LOCAL_PORT} --listen ${TARGET_PORT}'
|
||||
|
||||
# Option to connect with selenium server
|
||||
connect_to_grid = str_to_bool(str(os.getenv('CONNECT_TO_GRID', False)))
|
||||
logger.info('Connect with selenium grid? {input}'.format(input=connect_to_grid))
|
||||
appium_cmd = 'appium'
|
||||
if connect_to_grid:
|
||||
try:
|
||||
appium_host = os.getenv('APPIUM_HOST', '127.0.0.1')
|
||||
appium_port = int(os.getenv('APPIUM_PORT', 4723))
|
||||
selenium_host = os.getenv('SELENIUM_HOST', '172.17.0.1')
|
||||
selenium_port = int(os.getenv('SELENIUM_PORT', 4444))
|
||||
appium.create_node_config(CONFIG_FILE, emulator_name, android_version,
|
||||
appium_host, appium_port, selenium_host, selenium_port)
|
||||
appium_cmd += ' --nodeconfig {file}'.format(file=CONFIG_FILE)
|
||||
except ValueError as v_err:
|
||||
logger.error(v_err)
|
||||
|
||||
# Start installation of android packages, emulator creation and appium in a terminal
|
||||
android_cmd = get_android_bash_commands(android_version, emulator_type)
|
||||
if android_cmd:
|
||||
cmd = '({vnc}) & (xterm -T "Android-Appium" -n "Android-Appium" -e \"{android} && ' \
|
||||
'/bin/echo $emulator_name && appium\")'.format(vnc=vnc_cmd, android=android_cmd)
|
||||
'/bin/echo $EMULATOR_NAME && {appium}\")'.format(
|
||||
vnc=vnc_cmd, android=android_cmd, appium=appium_cmd)
|
||||
else:
|
||||
logger.warning('There is no android packages installed!')
|
||||
cmd = '({vnc}) & (xterm -e \"appium\")'.format(vnc=vnc_cmd)
|
||||
cmd = '({vnc}) & (xterm -e \"{appium}\")'.format(vnc=vnc_cmd, appium=appium_cmd)
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
|
||||
|
||||
|
@ -64,7 +88,7 @@ def get_item_position(keyword, items):
|
|||
"""
|
||||
Get position of item in array by given keyword.
|
||||
|
||||
:return: Item position.
|
||||
:return: item position
|
||||
:rtype: int
|
||||
"""
|
||||
pos = 0
|
||||
|
@ -129,6 +153,18 @@ def get_android_bash_commands(android_version, emulator_type):
|
|||
return bash_command
|
||||
|
||||
|
||||
def str_to_bool(str):
|
||||
"""
|
||||
Convert string to boolean.
|
||||
|
||||
:param str: given string
|
||||
:type str: str
|
||||
:return: converted string
|
||||
:rtype: bool
|
||||
"""
|
||||
return str.lower() in ('yes', 'true', 't', '1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger.setLevel(logging.INFO)
|
||||
run()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
"""Unit test for start.py."""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
import mock
|
||||
|
@ -9,14 +11,47 @@ from service import start
|
|||
class TestService(TestCase):
|
||||
"""Unit test class to test method run."""
|
||||
|
||||
@mock.patch('service.start.get_android_bash_commands')
|
||||
def setUp(self):
|
||||
os.environ['ANDROID_VERSION'] = '4.2.2'
|
||||
os.environ['EMULATOR_TYPE'] = start.TYPE_X86
|
||||
os.environ['CONNECT_TO_GRID'] = str(False)
|
||||
|
||||
@mock.patch('subprocess.check_call')
|
||||
def test_service(self, mocked_bash_cmd, mocked_subprocess):
|
||||
self.assertFalse(mocked_bash_cmd.called)
|
||||
@mock.patch('service.start.get_android_bash_commands')
|
||||
def test_without_selenium_server(self, mocked_subprocess, mocked_bash_cmd):
|
||||
self.assertFalse(mocked_subprocess.called)
|
||||
self.assertFalse(mocked_bash_cmd.called)
|
||||
start.run()
|
||||
self.assertTrue(mocked_bash_cmd.called)
|
||||
self.assertTrue(mocked_subprocess.called)
|
||||
self.assertTrue(mocked_bash_cmd.called)
|
||||
|
||||
@mock.patch('subprocess.check_call')
|
||||
@mock.patch('service.appium.create_node_config')
|
||||
@mock.patch('service.start.get_android_bash_commands')
|
||||
def test_with_selenium_server(self, mocked_subprocess, mocked_config, mocked_bash_cmd):
|
||||
os.environ['CONNECT_TO_GRID'] = str(True)
|
||||
self.assertFalse(mocked_subprocess.called)
|
||||
self.assertFalse(mocked_config.called)
|
||||
self.assertFalse(mocked_bash_cmd.called)
|
||||
start.run()
|
||||
self.assertTrue(mocked_subprocess.called)
|
||||
self.assertTrue(mocked_config.called)
|
||||
self.assertTrue(mocked_bash_cmd.called)
|
||||
|
||||
@mock.patch('subprocess.check_call')
|
||||
@mock.patch('service.appium.create_node_config')
|
||||
@mock.patch('service.start.get_android_bash_commands')
|
||||
def test_invalid_integer(self, mocked_subprocess, mocked_config, mocked_bash_cmd):
|
||||
os.environ['CONNECT_TO_GRID'] = str(True)
|
||||
os.environ['APPIUM_PORT'] = 'test'
|
||||
self.assertFalse(mocked_subprocess.called)
|
||||
self.assertFalse(mocked_config.called)
|
||||
self.assertFalse(mocked_bash_cmd.called)
|
||||
start.run()
|
||||
self.assertTrue(mocked_subprocess.called)
|
||||
self.assertFalse(mocked_config.called)
|
||||
self.assertTrue(mocked_bash_cmd.called)
|
||||
self.assertRaises(ValueError)
|
||||
|
||||
@mock.patch('service.start.get_android_bash_commands')
|
||||
@mock.patch('subprocess.check_call')
|
||||
|
@ -28,3 +63,11 @@ class TestService(TestCase):
|
|||
start.run()
|
||||
self.assertTrue(mocked_subprocess.called)
|
||||
self.assertTrue(mocked_logger_warning.called)
|
||||
|
||||
def tearDown(self):
|
||||
del os.environ['ANDROID_VERSION']
|
||||
del os.environ['EMULATOR_TYPE']
|
||||
if os.getenv('CONNECT_TO_GRID') == str(True):
|
||||
del os.environ['CONNECT_TO_GRID']
|
||||
if os.getenv('APPIUM_PORT'):
|
||||
del os.environ['APPIUM_PORT']
|
||||
|
|
17
service/tests/test_appium_config.py
Normal file
17
service/tests/test_appium_config.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
"""Unit test for appium.py."""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from service import CONFIG_FILE
|
||||
from service import appium
|
||||
|
||||
|
||||
class TestAppiumConfig(TestCase):
|
||||
"""Unit test class to test method create_node_config."""
|
||||
|
||||
def test_create_node_config(self):
|
||||
self.assertFalse(os.path.exists(CONFIG_FILE))
|
||||
appium.create_node_config(CONFIG_FILE, 'emulator_name', '4.2.2', '127.0.0.1', 4723, '127.0.0.1', 4444)
|
||||
self.assertTrue(os.path.exists(CONFIG_FILE))
|
||||
os.remove(CONFIG_FILE)
|
Loading…
Reference in a new issue