TLDR: я создаю несколько драйверов селена для тестирования различных функций, и это использует слишком много памяти. Есть ли способ ограничить использование памяти селеном? (Я уже бегу без головы)
Поэтому я использую селен с pytest для запуска тестов на своем веб-сайте. Для настройки правильной среды для запуска этих тестов я использую несколько функций/приспособлений, которые создают внутри них еще один драйвер и закрывают драйвер, когда они завершаются.
Но этот подход проблематичен для меня, поскольку он использует слишком много памяти и приводит к сбою моей машины каждый раз, когда я пытаюсь запустить/отладить. У меня есть несколько служебных функций в разных файлах, которые используются в моих тестах и создают собственный драйвер для выполнения своей работы. Я хочу знать, смогу ли я использовать один драйвер для выполнения этих служебных функций.
Вот тесты, которые я пытаюсь запустить.
from lib.driver_options import get_driver_options
import lib.resetEnvr as resetEnvr
import lib.sayTRUST_ClientGroup as groupmanager
#... other imports
@pytest.fixture(scope = "module")
def load_config():
file_path = os.path.dirname(__file__)
config_file_path = os.path.join(os.path.dirname(os.path.dirname(file_path)), 'config', 'website_config.json')
vz_config_path = os.path.join(os.path.dirname(os.path.dirname(file_path)), 'config',
'cloud_testserver.json')
with open(config_file_path) as f:
configurations = json.load(f)
with open(vz_config_path) as f:
vz_config = json.load(f)
default_config = configurations['default_config']
keyfile = os.path.join(os.path.dirname(os.path.dirname(file_path)), 'config', 'Service_PrivateKey_RSA')
return {
'default_config': default_config,
'vz_config': vz_config,
'keyfile': keyfile
}
class Test_NetworkAccess():
"""
Test cases:
Changing the network settings and making sure they all work.
"""
NATBUDDY_PRIVIP = "1.2.3.4"
NATBUDDY_PUBIP = "1.2.3.4"
TCPPORT = 8360
UDPPORT = 8361
config =None
@pytest.fixture(scope = "function")
def setup_environment(self, load_config):
print("setup method")
options = get_driver_options()
service = Service()
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(2)
vars = {}
# env reseter cleans up the websites state after each testing.
env_reseter = resetEnvr.dbCleaner()
ifaces = load_config['vz_config']["TemplateVM_info"]["network interfaces"]
for iface in ifaces:
for fixed_ip in iface.get("fixed_ips", []):
ip = fixed_ip.get("ip_address", "")
if ip.startswith("10.236."):
pub_ip = ip
else:
priv_ip = ip
# try to delete previous settings.
try:
groupmanager.delete_testClients()
except:
pass
try:
groupmanager.delete_testGroup()
except:
pass
yield {
'driver': driver,
'env_reseter': env_reseter,
'pub_ip': pub_ip,
'priv_ip': priv_ip,
'vars': vars
}
# Teardown after each test function
driver.close()
driver.quit()
@pytest.fixture(scope = "module", autouse=True)
def setup_iface(self):
print("setup interface")
print("add_listenPort")
groupmanager.add_listenPort()
print("add_private_iface")
groupmanager.add_private_iface()
env_reseter = resetEnvr.dbCleaner()
print("get backup")
env_reseter.getBackup()
@pytest.fixture(scope = "function")
def create_test_group(self,request, setup_environment):
print("create test group")
params = request.param
groupmanager.create_testGroup(**params)
print("create test group yields")
yield
# Teardown
groupmanager.delete_testGroup()
@pytest.fixture(scope = "function")
def create_test_client(self, request, create_test_group):
params = request.param
print("create test client")
groupmanager.create_testClient(**params)
print("create test client yields")
yield
# teardown
groupmanager.delete_testClients()
@pytest.mark.parametrize("create_test_group", [
{"NATBuddyPub": False, "TCP": True, "UDP": False, "SSH": True},
{"NATBuddyPub": True, "TCP": True, "UDP": False, "SSH": True},
# Add other configurations as needed
], indirect=True)
@pytest.mark.parametrize("create_test_client", [
{},
], indirect=True)
@pytest.mark.usefixtures("setup_environment","create_test_group","create_test_client")
def test_NATBuddyGroupPubSSH(self,setup_environment,create_test_group,create_test_client, record_xml_attribute ,request):
# do some testing without using Driber
print("do some testing")
Вот пример того, как другие служебные функции используют драйвер селена. И настройки я использую для драйвера. Все они используют драйвер одинаково.
def get_driver_options():
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440, 900")
options.add_argument('--ignore-certificate-errors')
options.add_argument('--allow-running-insecure-content')
options.add_argument("--disable-extensions")
options.add_argument("--proxy-server='direct://'")
options.add_argument("--proxy-bypass-list=*")
options.add_argument("--start-maximized")
options.add_argument('--disable-gpu')
options.add_argument('--disable-dev-shm-usage')
options.add_argument("--FontRenderHinting[none]")
options.add_argument('--no-sandbox')
options.add_argument('log-level=3')
options.add_argument('--ignore-ssl-errors=yes')
options.add_argument('--allow-insecure-localhost')
return options
def add_private_iface():
options = get_driver_options()
service = Service()
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(15)
website_url = default_config["website_url"]
driver.get(website_url)
# do some stuff
# add some private interface to website
driver.find_element(By.LINK_TEXT, "Logout").click()
driver.close()
driver.quit()
И когда я запускаю эти тесты, я наблюдаю, что Google Chrome начинает использовать сумасшедшую нагрузку на процессор, и моя машина зависает.
Ранее я пытался запустить эти функции с передачей драйвера в качестве параметра, но, похоже, это не сработало и выдало кучу странных ошибок. Попробовав заставить это работать, я решил сделать это таким образом, но теперь у меня есть сомнения. Можно ли сделать для всего этого один драйвер селена?
Или, может быть, мне чего-то не хватает, возможно, в драйвере есть что-то, что я забываю отключить между вызовами функций. Я уже использую драйвер Chrome без головы, поэтому не знаю других настроек для этого.
Я не думаю, что проблема связана с другой частью pyton, потому что я заметил, что несколько экземпляров Chrome запускаются в диспетчере задач во время работы программы, и это причина, которая ограничивает использование моего процессора.
ОБНОВЛЕНИЕ 1: я успешно реализовал синглтон. Но теперь проблема изменилась. Когда драйвер Singleton вызывается самим pytest, Selenium не может подключиться. На данный момент мое главное подозрение заключается в том, что pytest каким-то образом блокирует себя через многопоточность, хотя я пытался реализовать механизм блокировки для моего одноэлементного драйвера. Вот драйвер Singleton, который я сделал.
class SdriverMeta(type):
_instances = {}
_lock: Lock = Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class SDriver:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(SDriver, cls).__new__(cls)
options = get_driver_options()
service = Service()
cls._instance.driver =webdriver.Chrome(service=service, options=options)
cls._instance.driver.implicitly_wait(30)
return cls._instance
def get_driver(self):
return self.driver
def close_driver(self):
if self.driver:
self.driver.close()
self.driver.quit()
SDriver._instance = None
И вот в чем проблема. В одном из приспособлений для создания клиентской группы тестирования для надлежащего сертификационного тестирования я пытаюсь заставить селен открыть мой веб-сайт, но он выдает *MaxRetryError, превышено максимальное количество повторов с URL-адресом: /session/2369e6d74474ac5c8ab85e652b356e0c/url (вызвано NewConnectionError('< Объект urllib3.connection.HTTPConnection по адресу 0x0000027B36908B50>: Не удалось установить новое соединение: [WinError 10061] *
@pytest.fixture(scope = "function")
def create_test_group(self,request, setup_environment):
print("create test group")
params = request.param
groupmanager.create_testGroup(**params)# here we have the problem.
print("create test group yields")
yield
# Teardown steps if necessary (e.g., deleting the test group)
groupmanager.delete_testGroup()
def create_testGroup(NATBuddyPub=False, SSH=False, TCP=False, UDP=False):
'''
options = get_driver_options()
service = Service()
driver = webdriver.Chrome(service=service, options=options)
'''
sdriver=SDriver()
driver = sdriver.get_driver()
#driver.implicitly_wait(60)
website_url = default_config["website_url"]
driver.get(website_url) # this is where it actually messes up
# do some other stuff...
Итак, моя проблема с памятью, похоже, решена. Но на этот раз оно заменено на maxRetryError.
ОБНОВЛЕНИЕ 2: Эта ошибка была вызвана тем, что я неправильно вызвал драйвер синглтона класса и вместо этого вызвал self.
def get_driver(cls):
if cls._instance.driver is None:
options = get_driver_options()
service = Service()
cls._instance.driver = webdriver.Chrome(service=service, options=options)
cls._instance.driver.implicitly_wait(30)
return cls._instance.driver
это решило проблему выше. И все же я вернулся прямо к исходной точке. Если я запускаю код построчно, кажется, он работает нормально. Однако как только я запускаю его, он перегружает процессор и зависает мой компьютер.
ОБНОВЛЕНИЕ 3: В комментарии @Techrookie89 я добавил к своей машине дополнительные ядра, и это, похоже, сработало для части замораживания и разрушения. В некоторых тестах код содержал некоторую логику создания подпроцессов. Думаю, в этом была проблема.





Я не эксперт в питоне, но проблему, которую вы излагаете, можно решить с помощью паттерна проектирования Singleton. По сути, вы проверяете, является ли объект нулевым или нет, если он равен нулю? вы его инициализируете, а если это не так, вы возвращаете ранее инициализированный экземпляр.
Это должно быть что-то похожее на фрагмент ниже:
class WebBrowser:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(WebBrowser, cls).__new__(cls)
options = get_driver_options()
service = Service()
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(15)
cls._instance.driver = webdriver.Chrome()
return cls._instance
def get_driver(self):
return self.driver
def close_driver(self):
if self.driver:
self.driver.quit()
WebBrowser._instance = None
Затем это можно использовать в наших тестах, как показано ниже:
browser = WebBrowser().get_driver()
browser.get("https://duckduckgo.com")
Примечание. Если вы хотите, чтобы тип веб-драйвера также был динамическим, я бы посоветовал также прочитать шаблон проектирования Factory.
Можете ли вы поделиться сутью обновленного кода, который вы пробовали?
Действителен ли URL-адрес, который вы используете? Опубликованная вами ошибка, по-видимому, связана с сетевой ошибкой на вашей стороне, т. е. у браузера нет доступа к Интернету.
Какую конфигурацию машины вы используете? А параллелизм, которого вы пытаетесь достичь?
Это машина с ОС Windows 11, 4-ядерным процессором с частотой 5,6 ГГц и оперативной памятью 7,9 ГБ. Я использую Python 3.10 с Selenium версии 4.18.1, pythest 8.0.2 и драйвером Chrome 2.24.1. Также я использую последнюю версию pycharm.
Какое количество параллельных тестов вы выполняете? Вы пробовали это с 4 параллелизмом или более?
Они должны выполняться последовательно один за другим. Я ничего не делаю для параллельного запуска этих тестов
Хорошо, я пытался использовать этот шаблон проектирования Singleton для своего драйвера, но, похоже, он нарушает работу селена. Когда я пытаюсь получить URL-адрес с помощью add_private_iface(): или любой другой функции, например, он выдает мне MaxRetryError для подключения к локальному хосту для некоторого порта x.