How to use adb_shell method in ATX

Best Python code snippet using ATX

test_devices.py

Source:test_devices.py Github

copy

Full Screen

1import os2import pytest3from mock import MagicMock, Mock, call, patch4import AndroidRunner.Adb as Adb5from AndroidRunner.Device import Device6from AndroidRunner.Devices import Devices7from AndroidRunner.util import ConfigError8class TestDevice(object):9 @pytest.fixture()10 @patch('AndroidRunner.Adb.connect')11 def device(self, adb_connect):12 adb_connect.return_value = None13 name = 'fake_device'14 device_id = 12345678915 device_settings = {}16 return Device(name, device_id, device_settings)17 @pytest.fixture()18 @patch('AndroidRunner.Adb.connect')19 def device_root(self, adb_connect):20 adb_connect.return_value = None21 name = 'fake_device'22 device_id = 12345678923 device_settings = {'root_disable_charging': True,24 'charging_disabled_value': '0', 'usb_charging_disabled_file': 'test/file'}25 return Device(name, device_id, device_settings)26 @pytest.fixture()27 @patch('AndroidRunner.Adb.connect')28 def device_with_app_settings(self, adb_connect):29 adb_connect.return_value = None30 name = 'fake_device'31 device_id = 12345678932 device_settings = {"device_settings_reqs": {"app1": ["setting_1"], "app2": ["setting_1", "setting_2"]}}33 return Device(name, device_id, device_settings)34 @patch('AndroidRunner.Adb.connect')35 def test_init(self, adb_connect):36 name = 'fake_device'37 device_id = 12345678938 device_settings = {'root_disable_charging': True,39 'charging_disabled_value': '0',40 'usb_charging_disabled_file': 'test/file',41 'power_device': {42 'script_path': 'fake/path',43 'py_path': 'python',44 'vout': '3',45 'serial_num': '23'46 },47 'device_settings_reqs': {'app1': ['a, b'], 'app2': ['c']}48 }49 device = Device(name, device_id, device_settings)50 assert device.name == name51 assert device.id == device_id52 assert device.root_plug_value is None53 assert device.root_unplug_file == 'test/file'54 assert device.root_unplug_value == '0'55 assert device.root_unplug is True56 assert device.power_device is not None57 assert device.power_device['script_path'] == 'fake/path'58 assert device.power_device['py_path'] == 'python'59 assert device.device_settings_reqs == {'app1': ['a, b'], 'app2': ['c']}60 adb_connect.assert_called_once_with(device_id)61 @patch('AndroidRunner.Adb.configure_settings')62 @patch('logging.Logger.info')63 def test_configure_settings_device(self, logger, configure_settings, device_with_app_settings):64 device_with_app_settings.configure_settings_device("app1")65 logger.assert_called_with('Enabling setting_1')66 configure_settings.assert_called_with(device_with_app_settings.id, "setting_1",True)67 device_with_app_settings.configure_settings_device("app2")68 logger.assert_called_with('Enabling setting_2')69 device_with_app_settings.configure_settings_device(None)70 device_with_app_settings.configure_settings_device("app3")71 assert configure_settings.call_count == 372 @patch('AndroidRunner.Adb.shell')73 def test_get_version(self, adb_shell, device):74 adb_shell.return_value = 975 version = device.get_version()76 assert version == 977 adb_shell.assert_called_once_with(123456789, 'getprop ro.build.version.release')78 @patch('AndroidRunner.Adb.shell')79 def test_get_api_level(self, adb_shell, device):80 adb_shell.return_value = 2881 level = device.get_api_level()82 assert level == 2883 adb_shell.assert_called_once_with(123456789, 'getprop ro.build.version.sdk')84 @patch('AndroidRunner.Device.Device.get_app_list')85 def test_is_installed(self, get_app_list, device):86 get_app_list.return_value = ['app1', 'app2', 'installed_app']87 result_installed = device.is_installed(['app3', 'installed_app', 'app4'])88 assert len(result_installed) == 389 assert 'app3' in result_installed and not result_installed['app3']90 assert 'app4' in result_installed and not result_installed['app4']91 assert 'installed_app' in result_installed and result_installed['installed_app']92 @patch('AndroidRunner.Adb.list_apps')93 def test_get_app_list(self, adb_list_apps, device):94 adb_list_apps.return_value = ['app1', 'app2', 'app3']95 app_list = device.get_app_list()96 assert app_list == ['app1', 'app2', 'app3']97 @patch('AndroidRunner.Adb.install')98 def test_install_file_not_exist(self, adb_install, device):99 with pytest.raises(Adb.AdbError):100 device.install('fake.apk')101 assert adb_install.call_count == 0102 @patch('os.path.isfile')103 @patch('AndroidRunner.Adb.install')104 def test_install_file_exist(self, adb_install, os_isfile, device):105 os_isfile.return_value = True106 device.install('fake.apk')107 adb_install.assert_called_once_with(123456789, 'fake.apk')108 @patch('AndroidRunner.Adb.uninstall')109 def test_uninstall(self, adb_uninstall, device):110 app_name = 'fake_app'111 device.uninstall(app_name)112 adb_uninstall.assert_called_once_with(123456789, app_name)113 @patch('AndroidRunner.Device.Device.su_unplug')114 @patch('AndroidRunner.Device.Device.get_api_level')115 @patch('AndroidRunner.Adb.shell')116 def test_unplug_api_lower_23_no_root(self, adb_shell, get_api_level, su_unplug, device):117 get_api_level.return_value = 22118 device.unplug(False)119 assert su_unplug.call_count == 0120 adb_shell.assert_called_once_with(123456789, 'dumpsys battery set usb 0')121 @patch('AndroidRunner.Device.Device.su_unplug')122 @patch('AndroidRunner.Device.Device.get_api_level')123 @patch('AndroidRunner.Adb.shell')124 def test_unplug_api_higher_equal_23_no_root(self, adb_shell, get_api_level, su_unplug, device):125 get_api_level.return_value = 23126 device.unplug(False)127 assert su_unplug.call_count == 0128 adb_shell.assert_called_once_with(123456789, 'dumpsys battery unplug')129 @patch('AndroidRunner.Device.Device.su_unplug')130 @patch('AndroidRunner.Device.Device.get_api_level')131 @patch('AndroidRunner.Adb.shell')132 def test_unplug_api_lower_23_root(self, adb_shell, get_api_level, su_unplug, device_root):133 get_api_level.return_value = 22134 device_root.unplug(False)135 su_unplug.assert_called_once_with(False)136 assert adb_shell.call_count == 0137 @patch('AndroidRunner.Device.Device.su_unplug')138 @patch('AndroidRunner.Device.Device.get_api_level')139 @patch('AndroidRunner.Adb.shell')140 def test_unplug_api_lower_23_root_restart(self, adb_shell, get_api_level, su_unplug, device_root):141 get_api_level.return_value = 22142 device_root.unplug(True)143 su_unplug.assert_called_once_with(True)144 assert adb_shell.call_count == 0145 @patch('AndroidRunner.Device.Device.su_unplug')146 @patch('AndroidRunner.Device.Device.get_api_level')147 @patch('AndroidRunner.Adb.shell')148 def test_unplug_api_higher_equal_23_root(self, adb_shell, get_api_level, su_unplug, device_root):149 get_api_level.return_value = 23150 device_root.unplug(False)151 su_unplug.assert_called_once_with(False)152 assert adb_shell.call_count == 0153 @patch('AndroidRunner.Device.Device.su_unplug')154 @patch('AndroidRunner.Device.Device.get_api_level')155 @patch('AndroidRunner.Adb.shell')156 def test_unplug_api_higher_equal_23_root_restart(self, adb_shell, get_api_level, su_unplug, device_root):157 get_api_level.return_value = 23158 device_root.unplug(True)159 su_unplug.assert_called_once_with(True)160 assert adb_shell.call_count == 0161 @patch('AndroidRunner.Device.Device.check_plug_value')162 @patch('AndroidRunner.Adb.shell_su')163 def test_su_unplug_no_error(self, shell_su, check_plug_value, device_root):164 shell_su.side_effect = ['default_return', '']165 device_root.su_unplug(False)166 expected_calls = [call(device_root.id, 'cat %s' % device_root.root_unplug_file),167 call(device_root.id, 'echo %s > %s' %168 (device_root.root_unplug_value, device_root.root_unplug_file))]169 assert shell_su.mock_calls == expected_calls170 assert device_root.root_plug_value == 'default_return'171 assert check_plug_value.call_count == 0172 @patch('AndroidRunner.Device.Device.check_plug_value')173 @patch('AndroidRunner.Adb.shell_su')174 def test_su_unplug_not_rooted(self, shell_su, check_plug_value, device_root):175 shell_su.side_effect = ['su: not found', 'default_return', 'No such file or directory']176 with pytest.raises(Adb.AdbError):177 device_root.su_unplug(False)178 expected_calls = [call(device_root.id, 'cat test/file')]179 assert shell_su.mock_calls == expected_calls180 assert device_root.root_plug_value == 'su: not found'181 assert check_plug_value.call_count == 0182 @patch('AndroidRunner.Device.Device.check_plug_value')183 @patch('AndroidRunner.Adb.shell_su')184 def test_su_unplug_invalid_root_unplug_file(self, adb_shell, check_plug_value, device_root):185 adb_shell.side_effect = ['No such file or directory', '']186 with pytest.raises(ConfigError):187 device_root.su_unplug(False)188 expected_calls = [call(device_root.id, 'cat %s' % device_root.root_unplug_file)]189 assert adb_shell.mock_calls == expected_calls190 assert device_root.root_plug_value == 'No such file or directory'191 assert check_plug_value.call_count == 0192 @patch('AndroidRunner.Device.Device.check_plug_value')193 @patch('AndroidRunner.Adb.shell_su')194 def test_su_unplug_restart(self, shell_su, check_plug_value, device_root):195 shell_su.side_effect = ['default_return', '']196 device_root.su_unplug(True)197 expected_calls = [call(device_root.id, 'cat %s' % device_root.root_unplug_file),198 call(device_root.id, 'echo %s > %s' %199 (device_root.root_unplug_value, device_root.root_unplug_file))]200 assert shell_su.mock_calls == expected_calls201 assert device_root.root_plug_value == 'default_return'202 check_plug_value.assert_called_once()203 def test_check_plug_value_no_action(self, device_root):204 device_root.root_plug_value = 'enabled'205 device_root.root_unplug_value = 'disabled'206 device_root.check_plug_value()207 assert device_root.root_plug_value == 'enabled'208 assert device_root.root_unplug_value == 'disabled'209 def test_check_plug_value_unplug_plug_int_no_match(self, device_root):210 device_root.root_plug_value = 1211 device_root.root_unplug_value = 0212 device_root.check_plug_value()213 assert device_root.root_plug_value == 1214 assert device_root.root_unplug_value == 0215 @patch('logging.Logger.info')216 def test_check_plug_value_unplug_int_plug_string_no_match(self, logger, device_root):217 device_root.root_plug_value = 'enabled'218 device_root.root_unplug_value = 0219 device_root.check_plug_value()220 assert device_root.root_plug_value == 'enabled'221 assert device_root.root_unplug_value == 0222 logger.assert_called_once_with('Error setting root plug value, '223 'check manually after experiment if charging is enabled')224 def test_check_plug_value_same_plug_unplug_int(self, device_root):225 device_root.root_plug_value = 0226 device_root.root_unplug_value = 0227 device_root.check_plug_value()228 assert device_root.root_plug_value == 1229 assert device_root.root_unplug_value == 0230 def test_check_plug_value_same_plug_unplug_string_set_enabled(self, device_root):231 device_root.root_plug_value = 'disabled'232 device_root.root_unplug_value = 'disabled'233 device_root.check_plug_value()234 assert device_root.root_plug_value == 'enabled'235 assert device_root.root_unplug_value == 'disabled'236 def test_check_plug_value_same_plug_unplug_string_set_disabled(self, device_root):237 device_root.root_plug_value = 'enabled'238 device_root.root_unplug_value = 'enabled'239 device_root.check_plug_value()240 assert device_root.root_plug_value == 'disabled'241 assert device_root.root_unplug_value == 'enabled'242 @patch('AndroidRunner.Device.Device.su_plug')243 @patch('AndroidRunner.Adb.shell')244 def test_plug_no_root(self, adb_shell, su_plug, device):245 device.plug()246 assert su_plug.call_count == 0247 adb_shell.assert_called_once_with(123456789, 'dumpsys battery reset')248 @patch('AndroidRunner.Device.Device.su_plug')249 @patch('AndroidRunner.Adb.shell')250 def test_plug_root(self, adb_shell, su_plug, device_root):251 device_root.plug()252 su_plug.assert_called_once()253 adb_shell.assert_called_once_with(123456789, 'dumpsys battery reset')254 @patch('AndroidRunner.Adb.shell_su')255 def test_su_plug(self, adb_shell_su, device_root):256 device_root.root_plug_value = '123456'257 device_root.su_plug()258 adb_shell_su.assert_called_once_with(123456789, 'echo 123456 > test/file')259 @patch('AndroidRunner.Adb.shell')260 def test_current_activity_current_focus(self, adb_shell, device):261 adb_shell.return_value = 'mCurrentFocus=Window{28d47066 u0 com.android.chrome/org.chromium.chrome.browser.' \262 'ChromeTabbedActivity}\nmFocusedApp=Window{3078b3ad u0 com.sonyericsson.usbux/com.' \263 'sonyericsson.usbux.settings.ConnectivitySettingsActivity}'264 current_activity = device.current_activity()265 assert current_activity == 'com.android.chrome'266 @patch('AndroidRunner.Adb.shell')267 def test_current_activity_focused_app(self, adb_shell, device):268 adb_shell.return_value = 'mFocusedApp=AppWindowToken{ce6dd8c token=Token{31892bf ActivityRecord{21e5e0de u0 ' \269 'com.android.chrome/org.chromium.chrome.browser.ChromeTabbedActivity t25385}}}'270 current_activity = device.current_activity()271 assert current_activity == 'com.android.chrome'272 @patch('AndroidRunner.Adb.shell')273 def test_current_activity_none(self, adb_shell, device):274 adb_shell.return_value = 'mFocusedApp=null'275 current_activity = device.current_activity()276 assert current_activity is None277 @patch('AndroidRunner.Adb.shell')278 def test_current_activity_error(self, adb_shell, device):279 adb_shell.return_value = 'mFocusedApp=ajislvfhbljhglalkjasfdhdhg'280 with pytest.raises(Adb.AdbError):281 device.current_activity()282 @patch('AndroidRunner.Adb.shell')283 def test_launch_package_succes(self, adb_shell, device):284 package = 'fake.test.package'285 adb_shell.return_value = 'successsss'286 device.launch_package(package)287 adb_shell.assert_called_once_with(123456789, 'monkey -p {} 1'.format(package))288 @patch('AndroidRunner.Adb.shell')289 def test_launch_package_failure(self, adb_shell, device):290 package = 'fake.test.package'291 adb_shell.return_value = 'error error error monkey aborted error'292 with pytest.raises(Adb.AdbError):293 device.launch_package(package)294 adb_shell.assert_called_once_with(123456789, 'monkey -p {} 1'.format(package))295 @patch('AndroidRunner.Adb.shell')296 def test_launch_activity(self, adb_shell, device):297 package = 'fake.test.package'298 activity = 'main'299 device.launch_activity(package, activity)300 adb_shell.assert_called_once_with(123456789, 'am start -n {}/{}'.format(package, activity))301 @patch('AndroidRunner.Adb.shell')302 def test_launch_activity_force_stop(self, adb_shell, device):303 package = 'fake.test.package'304 activity = 'main'305 device.launch_activity(package, activity, force_stop=True)306 adb_shell.assert_called_once_with(123456789, 'am start -S -n {}/{}'.format(package, activity))307 @patch('AndroidRunner.Adb.shell')308 def test_launch_activity_action(self, adb_shell, device):309 package = 'fake.test.package'310 activity = 'main'311 device.launch_activity(package, activity, action='action')312 adb_shell.assert_called_once_with(123456789, 'am start -a {} -n {}/{}'.format('action', package, activity))313 @patch('AndroidRunner.Adb.shell')314 def test_launch_activity_data_uri(self, adb_shell, device):315 package = 'fake.test.package'316 activity = 'main'317 device.launch_activity(package, activity, data_uri='data.uri')318 adb_shell.assert_called_once_with(123456789, 'am start -n {}/{} -d {}'.format(package, activity, 'data.uri'))319 @patch('AndroidRunner.Adb.shell')320 def test_launch_activity_from_scratch(self, adb_shell, device):321 package = 'fake.test.package'322 activity = 'main'323 device.launch_activity(package, activity, from_scratch=True)324 adb_shell.assert_called_once_with(123456789,325 'am start -n {}/{} --activity-clear-task'.format(package, activity))326 @patch('AndroidRunner.Adb.shell')327 def test_force_stop(self, adb_shell, device):328 name = 'fake_app'329 device.force_stop(name)330 adb_shell.assert_called_once_with(123456789, 'am force-stop {}'.format(name))331 @patch('AndroidRunner.Adb.clear_app_data')332 def test_clear_app_data(self, adb_clear_app_data, device):333 name = 'fake_app'334 device.clear_app_data(name)335 adb_clear_app_data.assert_called_once_with(123456789, name)336 @patch('AndroidRunner.Adb.logcat')337 def test_logcat_to_file(self, adb_logcat, device, tmpdir):338 path = os.path.join(str(tmpdir), 'logcat')339 logcat_result = "test file content: 123dsfg564sdfhg"340 adb_logcat.return_value = logcat_result341 device.logcat_to_file(path)342 files_in_path = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]343 assert len(files_in_path) == 1344 with open(os.path.join(path, files_in_path[0]), 'r') as fl:345 file_content = fl.read()346 assert file_content == logcat_result347 adb_logcat.assert_called_once_with(123456789)348 @patch('AndroidRunner.Adb.logcat')349 def test_logcat_regex(self, adb_logcat, device):350 logcat_result = "test result 123dsfg564sdfhg"351 adb_logcat.return_value = logcat_result352 fake_regex = 'auiashdfdfv'353 result = device.logcat_regex(fake_regex)354 adb_logcat.assert_called_once_with(123456789, regex=fake_regex)355 assert result == logcat_result356 @patch('AndroidRunner.Adb.push')357 def test_push(self, adb_push, device):358 adb_push.return_value = 'pushpush'359 local_path = 'test/local/path'360 remote_path = 'test/remote/path'361 result = device.push(local_path, remote_path)362 adb_push.assert_called_once_with(123456789, local_path, remote_path)363 assert result == 'pushpush'364 @patch('AndroidRunner.Adb.pull')365 def test_pull(self, adb_pull, device):366 adb_pull.return_value = 'pullpull'367 local_path = 'test/local/path'368 remote_path = 'test/remote/path'369 result = device.pull(local_path, remote_path)370 adb_pull.assert_called_once_with(123456789, local_path, remote_path)371 assert result == 'pullpull'372 @patch('AndroidRunner.Adb.shell')373 def test_shell(self, adb_shell, device):374 adb_shell.return_value = 'shell return value'375 shell_command = 'dumpsys battery set usb 1'376 result = device.shell(shell_command)377 adb_shell.assert_called_once_with(123456789, shell_command)378 assert result == 'shell return value'379 @patch('AndroidRunner.Device.Device.get_api_level')380 @patch('AndroidRunner.Device.Device.get_version')381 def test_str(self, get_version, get_api_level, device):382 get_version.return_value = 9383 get_api_level.return_value = 28384 device_string = str(device)385 assert device_string == 'fake_device (123456789, Android 9, API level 28)'386class TestDevices(object):387 @pytest.fixture()388 @patch('AndroidRunner.Devices.load_json')389 @patch('AndroidRunner.Adb.setup')390 def devices(self, adb_setup, load_json):391 adb_setup.return_value = None392 load_json.return_value = {}393 return Devices([])394 @patch('AndroidRunner.Devices.load_json')395 @patch('AndroidRunner.Adb.setup')396 def test_init_error(self, adb_setup, load_json):397 load_json.return_value = {}398 with pytest.raises(ConfigError):399 Devices(['fake_device'])400 adb_setup.assert_called_once_with('adb')401 @patch('AndroidRunner.Device.Device.__init__')402 @patch('AndroidRunner.Devices.load_json')403 @patch('AndroidRunner.Adb.setup')404 def test_init_succes(self, adb_setup, load_json, device):405 device.return_value = None406 load_json.return_value = {'fake_device': 123456789}407 mock_device_settings = Mock()408 devices = Devices({'fake_device': mock_device_settings}, 'adb/path')409 adb_setup.assert_called_once_with('adb/path')410 device.assert_called_once_with('fake_device', 123456789, mock_device_settings)411 assert len(devices.devices) == 1412 assert isinstance(devices.devices[0], Device)413 def test_iter(self, devices):414 test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]415 devices.devices = test_list416 result_list = []417 for n in devices:418 result_list.append(n)419 assert result_list == test_list420 def test_get_device(self, devices):421 device_names = ['a', 'b', 'c', 'd']422 test_devices = []423 for name in device_names:424 mock_device = Mock()425 mock_device.name = name426 mock_device.id = name427 test_devices.append(mock_device)428 devices.devices = test_devices429 device = devices.get_device('c')430 assert device.name == 'c' and device.id == 'c'431 def test_names(self, devices):432 device_names = {'a': 1, 'b': 2, 'c': 3, 'd': 4}433 devices._device_map = device_names434 names = devices.names()435 assert len(names) == 4436 assert 'a' in names437 assert 'b' in names438 assert 'c' in names439 assert 'd' in names440 def test_ids(self, devices):441 device_names = {'a': 1, 'b': 2, 'c': 3, 'd': 4}442 devices._device_map = device_names443 ids = devices.ids()444 assert len(ids) == 4445 assert 1 in ids446 assert 2 in ids447 assert 3 in ids448 assert 4 in ids449 def test_get_id(self, devices):450 device_names = {'a': 1, 'b': 2, 'c': 3, 'd': 4}451 devices._device_map = device_names452 device_id = devices.get_id('c')453 assert device_id == 3454 def test_get_name(self, devices):455 device_names = {'a': 1, 'b': 2, 'c': 3, 'd': 4}456 devices._device_map = device_names457 name = devices.get_name(3)458 assert 'c' == name459class TestAdb(object):460 @patch('AndroidRunner.Adb.ADB')461 def test_setup_succes_custom_path(self, adb):462 adb_instance = MagicMock()463 adb_instance._ADB__error = None464 adb.return_value = adb_instance465 Adb.setup('adb/path')466 assert isinstance(Adb.adb, MagicMock)467 adb.assert_called_once_with(adb_path='adb/path')468 @patch('AndroidRunner.Adb.ADB')469 def test_setup_succes_default_path(self, adb):470 adb_instance = MagicMock()471 adb_instance._ADB__error = None472 adb.return_value = adb_instance473 Adb.setup()474 assert isinstance(Adb.adb, MagicMock)475 adb.assert_called_once_with(adb_path='adb')476 @patch('AndroidRunner.Adb.ADB')477 def test_setup_error(self, adb):478 adb_instance = MagicMock()479 adb_instance._ADB__error = True480 adb.return_value = adb_instance481 with pytest.raises(Adb.AdbError):482 Adb.setup()483 def test_connect_no_devices(self):484 mock_adb = Mock()485 mock_adb.get_devices.return_value = {}486 Adb.adb = mock_adb487 with pytest.raises(Adb.ConnectionError):488 Adb.connect('123')489 mock_adb.get_devices.assert_called_once()490 def test_connect_device_missing(self):491 mock_adb = Mock()492 mock_adb.get_devices.return_value = {'a': 12, 'b': 13}493 Adb.adb = mock_adb494 with pytest.raises(Adb.ConnectionError):495 Adb.connect(123)496 mock_adb.get_devices.assert_called_once()497 def test_connect_succes(self):498 mock_adb = Mock()499 mock_adb.get_devices.return_value = {'a': 12, 'b': 13, 'c': 123}500 Adb.adb = mock_adb501 Adb.connect(123)502 mock_adb.get_devices.assert_called_once()503 def test_shell_succes(self):504 mock_adb = Mock()505 mock_adb.shell_command.return_value = "succes "506 Adb.adb = mock_adb507 result = Adb.shell(123, "test_command")508 expected_calls = [call.set_target_by_name(123), call.shell_command('test_command')]509 assert mock_adb.mock_calls == expected_calls510 assert result == 'succes'511 def test_shell_error(self):512 mock_adb = Mock()513 mock_adb.shell_command.return_value = "error"514 Adb.adb = mock_adb515 with pytest.raises(Adb.AdbError):516 Adb.shell(123, "test_command")517 expected_calls = [call.set_target_by_name(123), call.shell_command('test_command')]518 assert mock_adb.mock_calls == expected_calls519 def test_shell_su_succes(self):520 mock_adb = Mock()521 mock_adb.shell_command.return_value = "su_succes "522 Adb.adb = mock_adb523 result = Adb.shell_su(123, "test_command_su")524 expected_calls = [call.set_target_by_name(123), call.shell_command('su -c \'test_command_su\'')]525 assert mock_adb.mock_calls == expected_calls526 assert result == 'su_succes'527 def test_shell_su_error(self):528 mock_adb = Mock()529 mock_adb.shell_command.return_value = "su_error"530 Adb.adb = mock_adb531 with pytest.raises(Adb.AdbError):532 Adb.shell_su(123, "test_command_su")533 expected_calls = [call.set_target_by_name(123), call.shell_command('su -c \'test_command_su\'')]534 assert mock_adb.mock_calls == expected_calls535 @patch('AndroidRunner.Adb.shell')536 def test_list_apps(self, adb_shell):537 adb_shell.return_value = 'package:com.app.1\npackage:com.app.2\npackage:com.app.3'538 result = Adb.list_apps(123)539 adb_shell.assert_called_once_with(123, 'pm list packages')540 assert len(result) == 3541 assert 'com.app.1' in result542 assert 'com.app.2' in result543 assert 'com.app.3' in result544 def test_install_default(self):545 mock_adb = Mock()546 mock_adb._ADB__output = 'succes'547 Adb.adb = mock_adb548 device_id = 123549 apk = 'test_apk.apk'550 result = Adb.install(device_id, apk)551 assert result == 'succes'552 expected_calls = [call.set_target_by_name(device_id), call.run_cmd('install -r -g {}'.format(apk))]553 assert mock_adb.mock_calls == expected_calls554 555 @patch('os.listdir')556 @patch('os.getcwd')557 def test_install_multiple_default(self, listdir, getcwd):558 mock_adb = Mock()559 mock_adb._ADB__output = 'succes'560 Adb.adb = mock_adb561 device_id = 123562 apk = 'test_apk.xapk'563 getcwd.return_value = '.'564 listdir.return_value = [apk]565 566 result = Adb.install(device_id, apk)567 assert result == 'succes'568 expected_calls = [call.set_target_by_name(device_id), call.run_cmd('install-multiple -r -g {}'.format(apk))]569 assert mock_adb.mock_calls == expected_calls570 def test_install_no_replace(self):571 mock_adb = Mock()572 mock_adb._ADB__output = 'succes'573 Adb.adb = mock_adb574 device_id = 123575 apk = 'test_apk.apk'576 result = Adb.install(device_id, apk, replace=False)577 assert result == 'succes'578 expected_calls = [call.set_target_by_name(device_id), call.run_cmd('install -g {}'.format(apk))]579 assert mock_adb.mock_calls == expected_calls580 def test_install_not_all_permissions(self):581 mock_adb = Mock()582 mock_adb._ADB__output = 'succes'583 Adb.adb = mock_adb584 device_id = 123585 apk = 'test_apk.apk'586 result = Adb.install(device_id, apk, all_permissions=False)587 assert result == 'succes'588 expected_calls = [call.set_target_by_name(device_id), call.run_cmd('install -r {}'.format(apk))]589 assert mock_adb.mock_calls == expected_calls590 @patch('AndroidRunner.Adb.success_or_exception')591 def test_uninstall_delete_data(self, s_or_e):592 mock_adb = Mock()593 mock_adb.uninstall.return_value = 'succes'594 Adb.adb = mock_adb595 device_id = 123596 name = 'app_name'597 manager = Mock()598 manager.attach_mock(s_or_e, "s_or_e_mock")599 manager.mock_adb = mock_adb600 Adb.uninstall(device_id, name)601 expected_calls = [call.mock_adb.set_target_by_name(123),602 call.mock_adb.uninstall(package=name, keepdata=True),603 call.s_or_e_mock('succes', '{}: "{}" uninstalled'.format(device_id, name),604 '{}: Failed to uninstall "{}"'.format(device_id, name))]605 assert manager.mock_calls == expected_calls606 @patch('AndroidRunner.Adb.success_or_exception')607 def test_uninstall_keep_data(self, s_or_e):608 mock_adb = Mock()609 mock_adb.uninstall.return_value = 'succes'610 Adb.adb = mock_adb611 device_id = 123612 name = 'app_name'613 manager = Mock()614 manager.attach_mock(s_or_e, "s_or_e_mock")615 manager.mock_adb = mock_adb616 Adb.uninstall(device_id, name, True)617 expected_calls = [call.mock_adb.set_target_by_name(123),618 call.mock_adb.uninstall(package=name, keepdata=False),619 call.s_or_e_mock('succes', '{}: "{}" uninstalled'.format(device_id, name),620 '{}: Failed to uninstall "{}"'.format(device_id, name))]621 assert manager.mock_calls == expected_calls622 @patch('AndroidRunner.Adb.success_or_exception')623 def test_clear_app_data(self, s_or_e):624 mock_adb = Mock()625 mock_adb.shell_command.return_value = 'succes'626 Adb.adb = mock_adb627 device_id = 123628 name = 'app_name'629 manager = Mock()630 manager.attach_mock(s_or_e, "s_or_e_mock")631 manager.mock_adb = mock_adb632 Adb.clear_app_data(device_id, name)633 expected_calls = [call.mock_adb.set_target_by_name(123),634 call.mock_adb.shell_command('pm clear app_name'),635 call.s_or_e_mock('succes', '{}: Data of "{}" cleared'.format(device_id, name),636 '{}: Failed to clear data for "{}"'.format(device_id, name))]637 assert manager.mock_calls == expected_calls638 @patch('logging.Logger.info')639 def test_success_or_exception_succes(self, logger):640 input_string = 'Success'641 succes_msg = 'action succes'642 fail_msg = 'action fail'643 Adb.success_or_exception(input_string, succes_msg, fail_msg)644 logger.assert_called_once_with(succes_msg)645 @patch('logging.Logger.info')646 def test_success_or_exception_exception(self, logger):647 input_string = 'fail'648 succes_msg = 'action succes'649 fail_msg = 'action fail'650 with pytest.raises(Adb.AdbError):651 Adb.success_or_exception(input_string, succes_msg, fail_msg)652 logger.assert_called_once_with(fail_msg + '\nMessage returned:\n{}'.format(input_string))653 def test_push(self):654 mock_adb = Mock()655 mock_adb._ADB__output = 'push output'656 Adb.adb = mock_adb657 device_id = 123658 local_path = 'local/path'659 remote_path = 'remote/path'660 result = Adb.push(device_id, local_path, remote_path)661 assert result == 'push output'662 expected_calls = [call.set_target_by_name(device_id),663 call.run_cmd('push {} {}'.format(local_path, remote_path))]664 assert mock_adb.mock_calls == expected_calls665 def test_pull_no_error(self):666 mock_adb = Mock()667 mock_adb._ADB__output = 'pull output'668 mock_adb._ADB__error = None669 Adb.adb = mock_adb670 device_id = 123671 local_path = 'local/path'672 remote_path = 'remote/path'673 result = Adb.pull(device_id, remote_path, local_path)674 assert result == 'pull output'675 expected_calls = [call.set_target_by_name(device_id),676 call.run_cmd('pull {} {}'.format(remote_path, local_path))]677 assert mock_adb.mock_calls == expected_calls678 def test_pull_error_no_bytes_in(self):679 mock_adb = Mock()680 mock_adb._ADB__output = 'pull output'681 mock_adb._ADB__error = 'error error'682 Adb.adb = mock_adb683 device_id = 123684 local_path = 'local/path'685 remote_path = 'remote/path'686 result = Adb.pull(device_id, remote_path, local_path)687 assert result == 'pull output'688 expected_calls = [call.set_target_by_name(device_id),689 call.run_cmd('pull {} {}'.format(remote_path, local_path))]690 assert mock_adb.mock_calls == expected_calls691 def test_pull_error_with_bytes_in(self):692 mock_adb = Mock()693 mock_adb._ADB__output = 'pull output'694 mock_adb._ADB__error = 'bytes in error'695 Adb.adb = mock_adb696 device_id = 123697 local_path = 'local/path'698 remote_path = 'remote/path'699 result = Adb.pull(device_id, remote_path, local_path)700 assert result == 'bytes in error'701 expected_calls = [call.set_target_by_name(device_id),702 call.run_cmd('pull {} {}'.format(remote_path, local_path))]703 assert mock_adb.mock_calls == expected_calls704 def test_logcat_no_regex(self):705 mock_adb = Mock()706 mock_adb.get_logcat.return_value = 'get_logcat output'707 Adb.adb = mock_adb708 device_id = 123709 result = Adb.logcat(device_id)710 assert result == 'get_logcat output'711 expected_calls = [call.set_target_by_name(device_id),712 call.get_logcat(lcfilter='-d')]713 assert mock_adb.mock_calls == expected_calls714 def test_logcat_with_regex(self):715 mock_adb = Mock()716 mock_adb.get_logcat.return_value = 'get_logcat output'717 Adb.adb = mock_adb718 test_regex = '[a-zA-Z]+'719 device_id = 123720 result = Adb.logcat(device_id, test_regex)721 assert result == 'get_logcat output'722 expected_calls = [call.set_target_by_name(device_id),723 call.get_logcat(lcfilter='-d -e {}'.format(test_regex))]724 assert mock_adb.mock_calls == expected_calls725 def test_reset_true(self):726 Adb.adb = Mock()727 cmd = True728 Adb.reset(cmd)729 expected_calls = [call.kill_server(), call.get_devices()]730 assert Adb.adb.mock_calls == expected_calls731 def test_reset_false(self):732 Adb.adb = Mock()733 cmd = False734 Adb.reset(cmd)735 expected_calls = []736 assert Adb.adb.mock_calls == expected_calls737 @patch('AndroidRunner.Adb.shell')738 def test_configure_settings(self, shell):739 Adb.adb = Mock()740 device_id = 123741 setting1 = "location_high_accuracy"742 setting2 = "location_gps_only"743 Adb.configure_settings(device_id, setting1, enable=True)744 shell.assert_called_with(123, "settings put secure location_providers_allowed +gps,network")745 Adb.configure_settings(device_id, setting2, enable=False)...

Full Screen

Full Screen

adb.py

Source:adb.py Github

copy

Full Screen

...67 log.debug("stdout is {}".format(stdout.strip()))68 log.debug("stderr is {}".format(stderr.decode('gbk')))69 result = [i.decode() for i in stdout.splitlines()]70 return [i for i in result if i and not i.startswith("* daemon")] # 过滤掉空的行,以及adb启动消息71 def adb_shell(self, *args):72 """adb shell命令入口73 :param args:74 :return:75 """76 args = ['shell'] + list(args)77 return self.adb(*args)78 def tap(self, x, y):79 self.adb_shell("input tap {} {}".format(x, y))80 def swipe(self, sx, sy, ex, ey, steps=100):81 self.adb_shell("input swipe {} {} {} {} {}".format(sx, sy, ex, ey, steps))82 def long_press(self, x, y, steps=1000):83 self.adb_shell("input swipe {} {} {} {} {}".format(x, y, x, y, steps))84 def devices(self):85 result = self.adb('devices')86 if len(result) == 1:87 return []88 log.debug(result)89 return [i.split()[0] for i in result if not i.startswith('List') and not i.startswith("adb")]90 @property91 @lru_cache()92 def resolution(self):93 """手机屏幕分辨率94 :return:95 """96 result = self.adb_shell("wm size")[0]97 result = result[result.find('size: ') + 6:] # 1080x180098 result = result.split('x')99 return [int(i) for i in result]100 @property101 @lru_cache()102 def orientation(self):103 for i in self.adb_shell('dumpsys', 'display'):104 index = i.find("orientation")105 if index != -1:106 return int(i[index + 12:index + 13])107 raise Exception("找不到orientation")108 @property109 def adb_remote(self):110 return self._adb_remote111 @property112 @lru_cache()113 def version(self):114 """adb 版本信息115 :return:116 """117 return self.adb('version')[0]118 @property119 def serial(self):120 return self._serial121 @property122 def android_version(self):123 return self.adb_shell('getprop ro.build.version.release')[0]124 @property125 def wlan_ip(self):126 """获取IP地址,基于 adb shell ifconfig127 :return:128 """129 for i in self.adb_shell('ifconfig'):130 i = i.strip()131 if i.startswith("inet addr") and i.find("Bcast") != -1:132 return i[i.find("inet addr:") + len("inet addr:"): i.find("Bcast")].strip()133 def screenshot(self, screenshot_dir, info="N"):134 """screencap方式截图135 :param screenshot_dir:136 :param info: 附加信息137 :return:138 """139 start_time = time.time()140 print("开始截图...")141 self.adb_shell("screencap -p /sdcard/screenshot.png")142 filename = '{0}-{1}.png'.format(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(start_time)), info)143 self.adb('pull /sdcard/screenshot.png {}\{}'.format(screenshot_dir, filename))144 print('截图已保存')145 return os.path.join(screenshot_dir, filename)146 def screenshot_ex(self, screenshot_dir, info='shot', compress=(0.5, 0.5)):147 """扩展screenshot函数,加入是否压缩和是否打开文件148 :param screenshot_dir:149 :param info:150 :param compress:151 :return:152 """153 png_file = self.screenshot(screenshot_dir, info)154 print('screenshot is {0}'.format(os.path.normpath(png_file)))155 if compress:156 im = Image.open(png_file)157 im_size = im.size158 im = im.resize((int(im_size[0] * compress[0]), int(im_size[1] * compress[1])), Image.ANTIALIAS) # 尺寸减少一半159 # im = im.rotate(270, expand=1) # 旋转角度是逆时针的,如果没expand,会出现旋转后,部分图像丢失160 compress_file = png_file.replace(r".png", r"_small.png")161 im.save(compress_file)162 return compress_file163 def screenshot_by_minicap(self, screenshot_dir, file_name="", scale=1.0):164 start_time = time.time()165 w, h = self.resolution166 r = self.orientation167 params = '{x}x{y}@{rx}x{ry}/{r}'.format(x=w, y=h, rx=int(w * scale), ry=int(h * scale), r=r * 90)168 self.adb_shell(169 '"LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -s -P {} > /sdcard/minicap-screenshot.jpg"'.format(170 params))171 if not file_name:172 file_name = '{0}.jpg'.format(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(start_time)))173 self.adb('pull /sdcard/minicap-screenshot.jpg {}\{}'.format(screenshot_dir, file_name))174 def get_app_mem_using(self, package_name=None):175 """获取内存占用176 :param package_name: app的包名177 :return: 返回app的内存总占用,单位MB178 """179 try:180 if not package_name:181 package_name = self.current_package_name182 log.debug(package_name)183 result = self.adb_shell("dumpsys meminfo {}".format(package_name))184 info = re.search('TOTAL\W+\d+', str(result)).group()185 result = info.split()186 return int(int(result[-1]) / 1000)187 except:188 import traceback189 traceback.print_exc()190 return 0191 def get_total_cpu_using(self):192 """获取手机当前CPU的总占用,不太准确,延迟很大193 :return:194 """195 cmd = 'dumpsys cpuinfo |{} "TOTAL"'.format(self._findstr)196 result = self.adb_shell(cmd)197 assert len(result) == 1198 result = result[0]199 cpu = 0200 try:201 cpu = float(result[:result.find('%')].strip())202 except:203 print(result)204 return cpu205 def get_app_cpu_using(self, pid=None):206 """采集当前运行的app的CPU占用,首次调用会延迟一秒统计出数据再返回207 :param pid:208 :return:209 """210 if not pid:211 pid = self.current_pid212 cmd = 'cat /proc/{}/stat'.format(pid)213 now = time.time()214 try:215 cpu = sum([int(i) for i in self.adb_shell(cmd)[0].split()[13:17]])216 if not self._func_data['cpu_cost']:217 self._func_data['cpu_cost'] = cpu218 self._func_data['cpu_cost_update_time'] = now219 time.sleep(1)220 return self.get_app_cpu_using(pid)221 else:222 cpu_use = cpu - self._func_data['cpu_cost']223 self._func_data['cpu_cost'] = cpu224 self._func_data['cpu_cost_update_time'] = now225 result = float("{:.2f}".format(cpu_use / (now - self._func_data['cpu_cost_update_time']) / 10))226 if result < 0:227 log.error("采集到的CPU占用数据异常:{}".format(result))228 return 0229 return result230 except:231 traceback.print_exc()232 return 0233 @property234 def current_package_info(self):235 result = self.adb_shell('dumpsys activity activities | {} mResumedActivity'.format(self._findstr))236 assert len(result) == 1, result237 return result[0].split()[-2].split("/")238 @property239 @lru_cache()240 def current_pid(self):241 result = self.adb_shell("ps|{} {}".format(self._findstr, self.current_package_name))242 log.info(result)243 return result[0].split()[1]244 @property245 def current_package_name(self):246 """获取当前运行app包名247 :return:248 """249 return self.current_package_info[0]250 @property251 def current_activity_name(self):252 """获取当前运行activity253 :return:254 """255 return self.current_package_info[1]256 def pull_file(self, remote, local):257 """从手机导出文件到本地 eg pull_file("/sdcard/screenshot.png", "1.png")258 :param remote:259 :param local:260 :return:261 """262 return self.adb('pull', remote, local)263 def push_file(self, local, remote):264 """上传文件到手机265 :param local:266 :param remote:267 :return:268 """269 return self.adb('push', local, remote)270 @staticmethod271 def start_server():272 log.debug('adb start-server')273 log.info(os.popen('adb.exe start-server').read())274 @staticmethod275 def kill_server():276 log.debug('adb kill-server')277 os.popen('adb.exe kill-server')278 @staticmethod279 def get_package_name_from_apk(apk_path):280 """从apk安装包中,获取包名281 :param apk_path: apk文件位置282 :return:283 """284 return ADB.get_apk_info_from_apk_file(apk_path)[0]285 @staticmethod286 def get_apk_info_from_apk_file(apk_path):287 """从apk安装包中,获取包名,和版本信息288 :param apk_path: apk文件位置289 :return:从 package: name='com.funsplay.god.anzhi' versionCode='10300' versionName='1.3.0' 中获取信息290 """291 result = "\n".join(run_cmd('aapt dump badging "{0}"'.format(apk_path)))292 package_name = result[result.index("name=\'") + 6:result.index("\' versionCode")]293 version_code = result[result.index("versionCode=\'") + 13:result.index("\' versionName")]294 version_name = result[result.index("versionName=\'") + 13:result.index("\' platformBuildVersionName")]295 return package_name, version_code, version_name296 def auto_install(self, path):297 """path可以是目录,自动安装目录下的全部apk,如果已经存在,则先卸载298 path也可以是具体apk路径299 :param path:300 :return:301 """302 if not os.path.exists(path):303 print('不存在的路径:{}'.format(path))304 return305 if os.path.isdir(path):306 print("当前连接的手机是:{0}".format(self._serial))307 for filename in os.listdir(path):308 if os.path.splitext(filename)[1].lower() == '.apk':309 self.install(os.path.join(path, filename))310 else:311 if os.path.splitext(path)[1].lower() == '.apk':312 self.install(path)313 else:314 print("文件后缀名不是apk")315 print('任务完成')316 def install(self, apk_path):317 print("发现apk文件:{0}".format(apk_path))318 dir_path, filename = os.path.split(apk_path)319 rename = False320 raw_apk_path = apk_path321 if is_chinese(filename):322 print("apk文件名存在中文,进行重命名")323 new_apk_path = os.path.join(dir_path, "{}.apk".format(int(time.time())))324 os.rename(raw_apk_path, new_apk_path)325 apk_path = new_apk_path326 rename = True327 package_name = self.get_package_name_from_apk(apk_path)328 print('apk安装包的包名是:{0}'.format(package_name))329 if self.is_install(package_name):330 print('手机中已经安装了该应用,准备移除')331 self.uninstall(package_name)332 else:333 print('手机未安装该应用')334 print("开始安装:{0}".format(apk_path))335 self.adb('install {}'.format(apk_path))336 if rename:337 os.rename(apk_path, raw_apk_path)338 def is_install(self, package_name):339 """检查手机中是否已经安装了某个apk340 :param package_name: 应用的包名341 :return:342 """343 return 'package:{0}'.format(package_name) in self.adb_shell("pm list package")344 def uninstall(self, package_name):345 """卸载apk346 :param package_name:347 :return:348 """349 self.adb("uninstall", package_name)350 def backup_current_apk(self, path=get_desktop_dir()):351 """导出当前正在运行的apk到指定目录,默认是桌面352 :param path:导出目录353 :return:354 """355 result = self.adb_shell("pm path", self.current_package_name)356 apk_path = result[0].strip().replace('package:', '')357 print('apk位置是:{0}'.format(apk_path))358 print('开始导出apk')359 apk_name = "{}{}.apk".format(self.current_package_name,360 time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time())))361 self.adb("pull {0} {1}\{2}".format(apk_path, path, apk_name))362 print("备份完成")363 def start_monkey(self, pct_touch=100, pct_motion=0, throttle=200, v='-v -v', times=100, logfile=None):364 if pct_touch + pct_motion != 100:365 raise Exception("Monkey各行为的配比总和超过了100")366 cmd = [367 'monkey',368 '-p {}'.format(self.current_package_name),369 '--pct-touch {}'.format(pct_touch),370 '--pct-motion {}'.format(pct_motion),371 '--throttle {}'.format(throttle),372 '{}'.format(v),373 '{}'.format(times)374 ]375 if logfile:376 cmd.append(r'> {}'.format(logfile))377 self.adb_shell(*iter(cmd))378 def stop_monkey(self):379 result = self.adb_shell("ps|{} monkey".format(self._findstr))380 if result:381 pid = result[0].split()[1]382 self.adb_shell('kill', pid)383 @staticmethod384 def raw_cmd(cmd):385 print('开始执行{}'.format(cmd))386 os.system(cmd)387 print('执行完成')388 def start_app(self, component):389 log.info(self.adb_shell("am start -n {}".format(component)))390 def stop_app(self, package):...

Full Screen

Full Screen

android_skp_capture.py

Source:android_skp_capture.py Github

copy

Full Screen

...55 device.startActivity(component=self.run_component)56 time.sleep(self.app_launch_delay)57 def kill(self):58 """Kill the app."""59 adb_shell('am force-stop %s' % self.package)60def check_output(cmd):61 """Convenience implementation of subprocess.check_output."""62 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)63 if proc.wait() != 0:64 raise Exception('Command failed: %s' % ' '.join(cmd))65 return proc.communicate()[0]66def adb_shell(cmd):67 """Run the given ADB shell command and emulate the exit code."""68 output = check_output(['adb', 'shell', cmd + '; echo $?']).strip()69 lines = output.splitlines()70 if lines[-1] != '0':71 raise Exception('ADB command failed: %s\n\nOutput:\n%s' % (cmd, output))72 return '\n'.join(lines[:-1])73def remote_file_exists(filename):74 """Return True if the given file exists on the device and False otherwise."""75 try:76 adb_shell('test -f %s' % filename)77 return True78 except Exception:79 return False80def capture_skp(skp_file, package, device):81 """Capture an SKP."""82 remote_path = '/data/data/%s/cache/%s' % (package, os.path.basename(skp_file))83 try:84 adb_shell('rm %s' % remote_path)85 except Exception:86 if remote_file_exists(remote_path):87 raise88 adb_shell('setprop debug.hwui.capture_frame_as_skp %s' % remote_path)89 try:90 # Spin, wait for the SKP to be written.91 timeout = 10 # Seconds92 start = time.time()93 device.drag((300, 300), (300, 350), 1, 10) # Dummy action to force a draw.94 while not remote_file_exists(remote_path):95 if time.time() - start > timeout:96 raise Exception('Timed out waiting for SKP capture.')97 time.sleep(1)98 # Pull the SKP from the device.99 cmd = ['adb', 'pull', remote_path, skp_file]100 check_output(cmd)101 finally:102 adb_shell('setprop debug.hwui.capture_frame_as_skp ""')103def load_app(filename):104 """Load the JSON file describing an app and return an App instance."""105 with open(filename) as f:106 app_dict = ast.literal_eval(f.read())107 return App(app_dict['name'],108 app_dict['package'],109 app_dict['activity'],110 app_dict['app_launch_delay'],111 app_dict['actions'])112def main():113 """Capture SKPs for all apps."""114 device = MonkeyRunner.waitForConnection()115 # TODO(borenet): Kill all apps.116 device.wake()...

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run ATX automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful