Best Python code snippet using lettuce-tools_python
test_firewall_driver.py
Source:test_firewall_driver.py
1# Copyright 2013 VMware, Inc2#3# Licensed under the Apache License, Version 2.0 (the "License"); you may4# not use this file except in compliance with the License. You may obtain5# a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the12# License for the specific language governing permissions and limitations13# under the License.14#15import contextlib16import mock17import webob.exc18from neutron import context19from neutron.db.firewall import firewall_db20from neutron.openstack.common import uuidutils21from neutron.plugins.vmware.vshield.common import exceptions as vcns_exc22from neutron.plugins.vmware.vshield import vcns_driver23from neutron.tests.unit.db.firewall import test_db_firewall24from neutron.tests.unit import vmware25from neutron.tests.unit.vmware.vshield import fake_vcns26_uuid = uuidutils.generate_uuid27VSE_ID = 'edge-1'28ROUTER_ID = '42f95450-5cc9-44e4-a744-1320e592a9d5'29VCNS_CONFIG_FILE = vmware.get_fake_conf("vcns.ini.test")30class VcnsDriverTestCase(test_db_firewall.FirewallPluginDbTestCase,31 firewall_db.Firewall_db_mixin):32 def vcns_firewall_patch(self):33 instance = self.mock_vcns.start()34 instance.return_value.update_firewall.side_effect = (35 self.fc2.update_firewall)36 instance.return_value.delete_firewall.side_effect = (37 self.fc2.delete_firewall)38 instance.return_value.update_firewall_rule.side_effect = (39 self.fc2.update_firewall_rule)40 instance.return_value.delete_firewall_rule.side_effect = (41 self.fc2.delete_firewall_rule)42 instance.return_value.add_firewall_rule_above.side_effect = (43 self.fc2.add_firewall_rule_above)44 instance.return_value.add_firewall_rule.side_effect = (45 self.fc2.add_firewall_rule)46 instance.return_value.get_firewall.side_effect = (47 self.fc2.get_firewall)48 instance.return_value.get_firewall_rule.side_effect = (49 self.fc2.get_firewall_rule)50 def setUp(self):51 self.config_parse(args=['--config-file', VCNS_CONFIG_FILE])52 # mock vcns53 self.fc2 = fake_vcns.FakeVcns(unique_router_name=False)54 self.mock_vcns = mock.patch(vmware.VCNS_NAME, autospec=True)55 self.vcns_firewall_patch()56 self.driver = vcns_driver.VcnsDriver(mock.Mock())57 super(VcnsDriverTestCase, self).setUp()58 self.addCleanup(self.fc2.reset_all)59 self.addCleanup(self.mock_vcns.stop)60 self.tenant_id = _uuid()61 self.subnet_id = _uuid()62class TestEdgeFwDriver(VcnsDriverTestCase):63 def _make_firewall_dict_with_rules(self, context, firewall_id):64 fw = self.get_firewall(context, firewall_id)65 fw_policy_id = fw['firewall_policy_id']66 if fw_policy_id:67 firewall_policy_db = self._get_firewall_policy(68 context, fw_policy_id)69 fw['firewall_rule_list'] = [70 self._make_firewall_rule_dict(fw_rule_db)71 for fw_rule_db in firewall_policy_db['firewall_rules']72 ]73 return fw74 def _compare_firewall_rule_lists(self, firewall_policy_id,75 list1, list2):76 for r1, r2 in zip(list1, list2):77 rule = r1['firewall_rule']78 rule['firewall_policy_id'] = firewall_policy_id79 for k in rule:80 self.assertEqual(rule[k], r2[k])81 def test_create_and_get_firewall(self):82 ctx = context.get_admin_context()83 name = 'firewall'84 with contextlib.nested(self.firewall_rule(name='fwr1',85 do_delete=False),86 self.firewall_rule(name='fwr2',87 do_delete=False),88 self.firewall_rule(name='fwr3',89 do_delete=False)) as fr:90 fw_rule_ids = [r['firewall_rule']['id'] for r in fr]91 with self.firewall_policy(firewall_rules=fw_rule_ids,92 do_delete=False) as fwp:93 fwp_id = fwp['firewall_policy']['id']94 with self.firewall(name=name,95 firewall_policy_id=fwp_id) as firewall:96 fw_create = firewall['firewall']97 fw_expect = self._make_firewall_dict_with_rules(98 ctx, fw_create['id'])99 self.driver.update_firewall(ctx, VSE_ID, fw_expect)100 fw_get = self.driver.get_firewall(ctx, VSE_ID)101 self._compare_firewall_rule_lists(102 fwp_id, fw_get['firewall_rule_list'],103 fw_expect['firewall_rule_list'])104 def test_update_firewall_with_rules(self):105 ctx = context.get_admin_context()106 name = 'new_firewall'107 with contextlib.nested(self.firewall_rule(name='fwr1',108 do_delete=False),109 self.firewall_rule(name='fwr2',110 do_delete=False),111 self.firewall_rule(name='fwr3',112 do_delete=False)) as fr:113 fw_rule_ids = [r['firewall_rule']['id'] for r in fr]114 with self.firewall_policy(firewall_rules=fw_rule_ids,115 do_delete=False) as fwp:116 fwp_id = fwp['firewall_policy']['id']117 with self.firewall(name=name,118 firewall_policy_id=fwp_id) as firewall:119 fw_create = firewall['firewall']120 fw_create = self._make_firewall_dict_with_rules(121 ctx, fw_create['id'])122 self.driver.update_firewall(ctx, VSE_ID, fw_create)123 data = {'firewall_rule': {'name': name,124 'source_port': '10:20',125 'destination_port': '30:40'}}126 self.new_update_request('firewall_rules', data,127 fr[0]['firewall_rule']['id'])128 fw_expect = self._make_firewall_dict_with_rules(129 ctx, fw_create['id'])130 self.driver.update_firewall(ctx, VSE_ID, fw_expect)131 fw_get = self.driver.get_firewall(132 ctx, VSE_ID)133 self._compare_firewall_rule_lists(134 fwp_id, fw_get['firewall_rule_list'],135 fw_expect['firewall_rule_list'])136 def test_delete_firewall(self):137 ctx = context.get_admin_context()138 name = 'firewall'139 with contextlib.nested(self.firewall_rule(name='fwr1',140 do_delete=False),141 self.firewall_rule(name='fwr2',142 do_delete=False),143 self.firewall_rule(name='fwr3',144 do_delete=False)) as fr:145 fw_rule_ids = [r['firewall_rule']['id'] for r in fr]146 with self.firewall_policy(firewall_rules=fw_rule_ids,147 do_delete=False) as fwp:148 fwp_id = fwp['firewall_policy']['id']149 with self.firewall(name=name,150 firewall_policy_id=fwp_id) as firewall:151 fw_create = firewall['firewall']152 fw_expect = self._make_firewall_dict_with_rules(153 ctx, fw_create['id'])154 self.driver.update_firewall(ctx, VSE_ID, fw_expect)155 self.driver.delete_firewall(ctx, VSE_ID)156 fw_get = self.driver.get_firewall(157 ctx, VSE_ID)158 self.assertFalse(fw_get['firewall_rule_list'])159 def test_update_firewall_rule(self):160 ctx = context.get_admin_context()161 name = 'new_firewall'162 with contextlib.nested(self.firewall_rule(name='fwr1',163 do_delete=False)) as fr:164 fw_rule_ids = [r['firewall_rule']['id'] for r in fr]165 with self.firewall_policy(firewall_rules=fw_rule_ids,166 do_delete=False) as fwp:167 fwp_id = fwp['firewall_policy']['id']168 with self.firewall(name=name,169 firewall_policy_id=fwp_id) as firewall:170 fw_create = firewall['firewall']171 fw_create = self._make_firewall_dict_with_rules(172 ctx, fw_create['id'])173 self.driver.update_firewall(ctx, VSE_ID, fw_create)174 data = {'firewall_rule': {'name': name,175 'source_port': '10:20',176 'destination_port': '30:40'}}177 req = self.new_update_request(178 'firewall_rules', data,179 fr[0]['firewall_rule']['id'])180 res = self.deserialize(self.fmt,181 req.get_response(self.ext_api))182 rule_expect = res['firewall_rule']183 rule_expect['edge_id'] = VSE_ID184 self.driver.update_firewall_rule(185 ctx, rule_expect['id'], VSE_ID, rule_expect)186 rule_get = self.driver.get_firewall_rule(187 ctx, rule_expect['id'], VSE_ID)188 for k, v in rule_get['firewall_rule'].items():189 self.assertEqual(rule_expect[k], v)190 def test_delete_firewall_rule(self):191 ctx = context.get_admin_context()192 name = 'new_firewall'193 with contextlib.nested(self.firewall_rule(name='fwr1',194 do_delete=False),195 self.firewall_rule(name='fwr2',196 do_delete=False)) as fr:197 fw_rule_ids = [r['firewall_rule']['id'] for r in fr]198 with self.firewall_policy(firewall_rules=fw_rule_ids,199 do_delete=False) as fwp:200 fwp_id = fwp['firewall_policy']['id']201 with self.firewall(name=name,202 firewall_policy_id=fwp_id) as firewall:203 fw_create = firewall['firewall']204 fw_create = self._make_firewall_dict_with_rules(205 ctx, fw_create['id'])206 self.driver.update_firewall(ctx, VSE_ID, fw_create)207 fr[0]['firewall_rule']['edge_id'] = VSE_ID208 self.driver.delete_firewall_rule(209 ctx, fr[0]['firewall_rule']['id'],210 VSE_ID)211 self.assertRaises(vcns_exc.VcnsNotFound,212 self.driver.get_firewall_rule,213 ctx, fr[0]['firewall_rule']['id'],214 VSE_ID)215 def test_insert_rule(self):216 ctx = context.get_admin_context()217 with self.firewall_policy() as fwp:218 fwp_id = fwp['firewall_policy']['id']219 with self.firewall(firewall_policy_id=fwp_id) as firewall:220 fw_create = firewall['firewall']221 fw_create = self._make_firewall_dict_with_rules(222 ctx, fw_create['id'])223 self.driver.update_firewall(ctx, VSE_ID, fw_create)224 with contextlib.nested(self.firewall_rule(name='fwr0',225 do_delete=False),226 self.firewall_rule(name='fwr1',227 do_delete=False),228 self.firewall_rule(name='fwr2',229 do_delete=False),230 self.firewall_rule(name='fwr3',231 do_delete=False),232 self.firewall_rule(name='fwr4',233 do_delete=False),234 self.firewall_rule(name='fwr5',235 do_delete=False),236 self.firewall_rule(237 name='fwr6',238 do_delete=False)) as fwr:239 # test insert when rule list is empty240 fwr0_id = fwr[0]['firewall_rule']['id']241 self._rule_action('insert', fwp_id, fwr0_id,242 insert_before=None,243 insert_after=None,244 expected_code=webob.exc.HTTPOk.code)245 fw_update = self._make_firewall_dict_with_rules(246 ctx, fw_create['id'])247 self.driver.update_firewall(ctx, VSE_ID, fw_update)248 # test insert at top of list above existing rule249 fwr1_id = fwr[1]['firewall_rule']['id']250 self._rule_action('insert', fwp_id, fwr1_id,251 insert_before=fwr0_id,252 insert_after=None,253 expected_code=webob.exc.HTTPOk.code)254 fw_expect = self._make_firewall_dict_with_rules(255 ctx, fw_create['id'])256 rule_info = {'firewall_rule_id': fwr1_id,257 'insert_before': fwr0_id,258 'insert_after': None}259 rule = fwr[1]['firewall_rule']260 self.driver.insert_rule(ctx, rule_info, VSE_ID, rule)261 fw_get = self.driver.get_firewall(262 ctx, VSE_ID)263 self._compare_firewall_rule_lists(264 fwp_id, fw_get['firewall_rule_list'],265 fw_expect['firewall_rule_list'])266 # test insert at bottom of list267 fwr2_id = fwr[2]['firewall_rule']['id']268 self._rule_action('insert', fwp_id, fwr2_id,269 insert_before=None,270 insert_after=fwr0_id,271 expected_code=webob.exc.HTTPOk.code)272 fw_expect = self._make_firewall_dict_with_rules(273 ctx, fw_create['id'])274 rule_info = {'firewall_rule_id': fwr2_id,275 'insert_before': None,276 'insert_after': fwr0_id}277 rule = fwr[2]['firewall_rule']278 self.driver.insert_rule(ctx, rule_info, VSE_ID, rule)279 fw_get = self.driver.get_firewall(280 ctx, VSE_ID)281 self._compare_firewall_rule_lists(282 fwp_id, fw_get['firewall_rule_list'],283 fw_expect['firewall_rule_list'])284 # test insert in the middle of the list using285 # insert_before286 fwr3_id = fwr[3]['firewall_rule']['id']287 self._rule_action('insert', fwp_id, fwr3_id,288 insert_before=fwr2_id,289 insert_after=None,290 expected_code=webob.exc.HTTPOk.code)291 fw_expect = self._make_firewall_dict_with_rules(292 ctx, fw_create['id'])293 rule_info = {'firewall_rule_id': fwr3_id,294 'insert_before': fwr2_id,295 'insert_after': None}296 rule = fwr[3]['firewall_rule']297 self.driver.insert_rule(ctx, rule_info, VSE_ID, rule)298 fw_get = self.driver.get_firewall(299 ctx, VSE_ID)300 self._compare_firewall_rule_lists(301 fwp_id, fw_get['firewall_rule_list'],302 fw_expect['firewall_rule_list'])303 # test insert in the middle of the list using304 # insert_after305 fwr4_id = fwr[4]['firewall_rule']['id']306 self._rule_action('insert', fwp_id, fwr4_id,307 insert_before=None,308 insert_after=fwr3_id,309 expected_code=webob.exc.HTTPOk.code)310 fw_expect = self._make_firewall_dict_with_rules(311 ctx, fw_create['id'])312 rule_info = {'firewall_rule_id': fwr4_id,313 'insert_before': None,314 'insert_after': fwr3_id}315 rule = fwr[4]['firewall_rule']316 self.driver.insert_rule(ctx, rule_info, VSE_ID, rule)317 fw_get = self.driver.get_firewall(318 ctx, VSE_ID)319 self._compare_firewall_rule_lists(320 fwp_id, fw_get['firewall_rule_list'],321 fw_expect['firewall_rule_list'])322 # test insert when both insert_before and323 # insert_after are set324 fwr5_id = fwr[5]['firewall_rule']['id']325 self._rule_action('insert', fwp_id, fwr5_id,326 insert_before=fwr4_id,327 insert_after=fwr4_id,328 expected_code=webob.exc.HTTPOk.code)329 fw_expect = self._make_firewall_dict_with_rules(330 ctx, fw_create['id'])331 rule_info = {'firewall_rule_id': fwr5_id,332 'insert_before': fwr4_id,333 'insert_after': fwr4_id}334 rule = fwr[5]['firewall_rule']335 self.driver.insert_rule(ctx, rule_info, VSE_ID, rule)336 fw_get = self.driver.get_firewall(337 ctx, VSE_ID)338 self._compare_firewall_rule_lists(339 fwp_id, fw_get['firewall_rule_list'],...
mixins.py
Source:mixins.py
1# Copyright 2015 Alcatel-Lucent USA Inc.2#3# Licensed under the Apache License, Version 2.0 (the "License"); you may4# not use this file except in compliance with the License. You may obtain5# a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the12# License for the specific language governing permissions and limitations13# under the License.14"""15These mixins extend BaseTestCase, so your testclass should only extend the16mixins it needs. The reason for this design is that now every mixin a test17extends will have 'setup_clients' called automagically for you. A test class'18structure would look like:19 BaseTestCase20 |21 BaseMixin22 / | \23mixin1 mixin2 mixin324 \ | /25 TestClass26"""27import contextlib2829from tempest.common.utils import data_utils30from tempest.test import BaseTestCase31from . import bgpvpn_client3233class BaseMixin(BaseTestCase):34 """Base class for all Mixins.3536 This class exists because calling get_client_manager() in every mixin would37 reinitialize all the clients over and over again. So don't use38 get_client_manager in the mixins, but cls.manager and cls.admin_manager39 instead.40 """41 @classmethod42 def setup_clients(cls):43 super(BaseMixin, cls).setup_clients()44 cls.manager = cls.get_client_manager()45 cls.admin_manager = cls.get_client_manager(credential_type='admin')46 cls.manager.bgpvpn_client = bgpvpn_client.BGPVPNClient(47 cls.manager.auth_provider)48 cls.admin_manager.bgpvpn_client = bgpvpn_client.BGPVPNClient(49 cls.manager.auth_provider)50 cls.manager.net_assoc_client = bgpvpn_client.BGPVPNNetworkAssociationClient(51 cls.manager.auth_provider)52 cls.admin_manager.net_assoc_client = bgpvpn_client.BGPVPNNetworkAssociationClient(53 cls.manager.auth_provider)54 cls.manager.rtr_assoc_client = bgpvpn_client.BGPVPNRouterAssociationClient(55 cls.manager.auth_provider)56 cls.admin_manager.rtr_assoc_client = bgpvpn_client.BGPVPNRouterAssociationClient(57 cls.manager.auth_provider)5859class BGPVPNMixin(BaseMixin):6061 @classmethod62 def setup_clients(cls):63 super(BGPVPNMixin, cls).setup_clients()64 cls.bgpvpn_client = cls.manager.bgpvpn_client65 cls.bgpvpn_client_admin = cls.admin_manager.bgpvpn_client66 cls.net_assoc_client = cls.manager.net_assoc_client67 cls.net_assoc_client_admin = cls.admin_manager.net_assoc_client68 cls.rtr_assoc_client = cls.manager.rtr_assoc_client69 cls.rtr_assoc_client_admin = cls.admin_manager.rtr_assoc_client7071 @contextlib.contextmanager72 def bgpvpn(self, do_delete=True, as_admin=True, **kwargs):73 client = self.bgpvpn_client_admin if as_admin else self.bgpvpn_client74 bgpvpn = {'name': data_utils.rand_name('bgpvpn')}75 bgpvpn.update(kwargs)76 bgpvpn = client.create_bgpvpn(**bgpvpn)77 try:78 yield bgpvpn79 finally:80 if do_delete:81 client.delete_bgpvpn(bgpvpn['id'])8283 @contextlib.contextmanager84 def router_assocation(self, router_id, bgpvpn_id, do_delete=True,85 as_admin=False, **kwargs):86 client = (self.rtr_assoc_client_admin if as_admin87 else self.rtr_assoc_client)88 rtr_assoc = {'router_id': router_id}89 rtr_assoc.update(kwargs)90 rtr_assoc = client.create_router_assocation(bgpvpn_id, **rtr_assoc)91 try:92 yield rtr_assoc93 finally:94 if do_delete:95 client.delete_router_assocation(rtr_assoc['id'], bgpvpn_id)9697 @contextlib.contextmanager98 def network_assocation(self, network_id, bgpvpn_id, do_delete=True,99 as_admin=False, **kwargs):100 client = (self.net_assoc_client_admin if as_admin101 else self.net_assoc_client)102 net_assoc = {'network_id': network_id}103 net_assoc.update(kwargs)104 net_assoc = client.create_network_association(bgpvpn_id, **net_assoc)105 try:106 yield net_assoc107 finally:108 if do_delete:109 client.delete_network_association(net_assoc['id'], bgpvpn_id)110111112class NetworkMixin(BaseMixin):113114 @classmethod115 def setup_clients(cls):116 super(NetworkMixin, cls).setup_clients()117 cls.network_client = cls.manager.network_client118 cls.network_client_admin = cls.admin_manager.network_client119 cls.networks_client = cls.manager.networks_client120 cls.networks_client_admin = cls.admin_manager.networks_client121 cls.subnets_client = cls.manager.subnets_client122 cls.subnets_client_admin = cls.admin_manager.subnets_client123124 @contextlib.contextmanager125 def network(self, do_delete=True, as_admin=False, **kwargs):126 client = (self.networks_client_admin if as_admin127 else self.networks_client)128 network = {'name': data_utils.rand_name('network')}129 network.update(kwargs)130 network = client.create_network(**network)['network']131 try:132 yield network133 finally:134 if do_delete:135 client.delete_network(network['id'])136137 @contextlib.contextmanager138 def subnet(self, cidr, do_delete=True, as_admin=False, **kwargs):139 client = self.subnets_client_admin if as_admin else self.subnets_client140 subnet = {'name': data_utils.rand_name('subnet'),141 'cidr': cidr}142 subnet.update(kwargs)143 subnet = client.create_subnet(**subnet)['subnet']144 try:145 yield subnet146 finally:147 if do_delete:148 client.delete_subnet(subnet['id'])149150 @contextlib.contextmanager151 def port(self, network_id, do_delete=True, as_admin=False, **kwargs):152 client = self.network_client_admin if as_admin else self.network_client153 port = {'name': data_utils.rand_name('port'),154 'network_id': network_id}155 port.update(kwargs)156 port = client.create_port(**port)['port']157 try:158 yield port159 finally:160 if do_delete:161 client.delete_port(port['id'])162163164class L3Mixin(BaseMixin):165166 @classmethod167 def setup_clients(cls):168 super(L3Mixin, cls).setup_clients()169 cls.routers_client = cls.manager.routers_client170 cls.routers_client_admin = cls.admin_manager.routers_client171172 @contextlib.contextmanager173 def router(self, do_delete=True, as_admin=False, **kwargs):174 client = self.routers_client_admin if as_admin else self.routers_client175 router = {'name': data_utils.rand_name('router')}176 router.update(kwargs)177 router = client.create_router(**router)['router']178 try:179 yield router180 finally:181 if do_delete:
...
eventhandlers.py
Source:eventhandlers.py
1from bungenicms.publisher import config2from bungenicms.publisher.utils import get_workflow_name3from bungenicms.publisher.utils import is_temporary, is_action_possible4from plone.app.linkintegrity.interfaces import ILinkIntegrityInfo5_marker = '_publisher_event_already_handled'6def publish_after_transition(obj, event):7 """ This event handler is executed after each transition and8 publishes the object with ftw.publisher on certain transitions.9 Also when retracting an object, the object will be published,10 since we should not delete anything unless it's delete from the11 sender instance too. This is necessary for preventing12 inconsistency, which could occur when deleting a folder which13 contains published objects on the reciever site.14 """15 # the event handler will be run multiple times, so we need to16 # remember which event we've already handled.17 if getattr(event, _marker, False):18 return19 else:20 setattr(event, _marker, True)21 # when there is no transition for the state change, we do nothing22 if not event.transition:23 return24 # do nothing with temprorary objects25 if is_temporary(obj):26 return27 # check if we should handle this transaction28 wf_transition = (event.workflow.__name__, event.transition.__name__)29 if wf_transition in config.PUSH_TRANSITIONS:30 action = 'push'31 elif wf_transition in config.DELETE_TRANSITIONS:32 action = 'delete'33 else:34 return35 if is_action_possible(obj, action):36 if action == 'push':37 obj.restrictedTraverse('publisher.publish')()38 elif action == 'delete':39 obj.restrictedTraverse('publisher.delete')()40def handle_remove_event(obj, event):41 """If an object will be removed on the senders instance, we need to create a42 publisher delete job.43 """44 # the event is notified for every subobject, but we only want to check45 # the top object which the users tries to delete46 if obj is not event.object:47 return48 workflow = get_workflow_name(obj)49 if not workflow or workflow not in config.PUBLISHING_WORKFLOWS:50 # we don't have a workflow or the workflow does not publish ever - so we51 # don't need to delete anything on the receiver.52 return53 # the event handler is fired twice (once from link integrity check), but54 # we just want to do our stuff once. And we should only do it if the user55 # already did confirm.56 do_delete = False57 request = getattr(obj, 'REQUEST', None)58 if request is None:59 do_delete = True60 else:61 info = ILinkIntegrityInfo(request)62 if not info.integrityCheckingEnabled():63 do_delete = True64 elif info.isConfirmedItem(obj):65 do_delete = True66 if request.URL.endswith('/sl_delete_object'):67 do_delete = True68 if request.has_key('form.submitted') and \69 request.URL.endswith('/delete_confirmation'):70 do_delete = True71 if request.URL.endswith('/folder_delete'):72 do_delete = True73 if request.has_key('form.button.Cancel'):74 do_delete = True75 # register the job76 if do_delete:...
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.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!