Python Playwright 如何保持登录状态

在编写UI自动化测试用例的时候,通常会采用每个测试用例前打开新页面重新进行登录,以减少用例间的影响,比如一个测试用例执行失败会影响到下一个测试用例的执行,或者下一个用例的开始依赖于上一个用例的结束页面。但是这种方式会使得测试用例的执行时间大幅度上升,尤其是在测试用例划分的颗粒度比较小的时候;加入一个项目中有2000个测试用例,登录操作耗时2秒,那么光耗费在登录上面的时间就有4000秒,达到一个多小时了,严重影响测试执行效率,再假如项目中使用了验证码限制登录的情况,那么就更复杂了。所以,如果可以将登录状态保持住,开始测试用例的时候打开新页面,跳过登录直接进入到待测系统中,就可以大幅度提高测试执行效率。通常,每种UI自动化测试工具都会有类似的功能,这里以Playwright为例来介绍如何实现。

 

功能实现

在Playwright中提供了现成的方法,通过 context.storage_state(path='<文件路径>') ,可以将当前浏览器上下文的全部状态保存下来,在创建浏览器上下文时,添加 storage_state 参数即可读取保存的文件,从而完全恢复之前的浏览器状态。示例代码如下

# 首次登陆系统
from playwright.sync_api import sync_playwright

with sync_playwright() as playwright:
  browser = playwright.chromium.launch()
  context = browser.new_context()
  page = context.new_page()

# 登陆系统
  page.goto('<login url>')
  page.fill('<username>', '<username selector>')
  page.fill('<password>', '<password selector>')
  page.click('<login button selector>')

# 判断是否登陆成功
  assert 'Welcome' in page.title()

# 保存状态文件
  context.storage_state(path='login_data.json')
# 使用已保存的状态文件跳过登录状态直接访问系统
with sync_playwright() as playwright:
  browser = playwright.chromium.launch()

# 创建浏览器上下文时加载状态文件
  context = browser.new_context(storage_state='login_data.json')
  page = context.new_page()

# 直接访问登录后的URL
  page.goto('<welcome url>')

# 判断是否访问到登录后的页面
  assert 'Welcome' in page.title()

 

结合Pytest

通过以上代码验证了这种实现方式的可用性,还是很好用的。结合Pytest测试框架,可以通过fixture的形式提供一个已登录的page对象,可以直接在测试用例中使用,实现方式如下:

@pytest.fixture()
def logged_page():
  ss_file = 'login_data.json'
  with sync_playwright() as playwright:
      browser = playwright.chromium.launch()

  # 判断是否存在状态文件,有的话就加载
      if os.path.isfile(ss_file):
          context = browser.new_context(storage_state=ss_file)
      else:
          context = browser.new_context()
      page = context.new_page()

  # 直接跳转至登录后页面,前提是未登录用户访问待测系统会自动跳转至登录页面,如果你的系统逻辑不一样,需要修改
      page.goto('<welcome url>')

  # 通过title判断是否成功进入系统,如果没有需要进行登录,一般在第一次访问系统,或登录信息过期等原因会触发
      if 'Welcome' not in page.title():
          page.fill('<username>', '<username selector>')
          page.fill('<password>', '<password selector>')
          page.click('<login button selector>')
      yield page

  # 测试执行结束后保存状态文件,前提是测试用例中不能退出系统,安全起见加上异常处理
  try:
         context.storage_state(path=ss_file)
      except Exception as e:
          print(e)

 

结合Clent-Page Object模式

如前,结合Pytest已经可以实现一个很好用的fixture,在很多场景下已经够用了,不过我在项目中结合Clent-Page Object模式进行了另一种实现,核心代码如下:

class Client(ABC):
  playwright = None
  browser = None

  def __init__(self, url: str, *, storage_state_name: str = None):
      self.url = url
      self.context = None
      self.main_page = None
      self.storage_state_file_path = None
      if storage_state_name is not None:
          self.storage_state_file_path = os.path.join('storage_state', f'{storage_state_name}.json')

  @abstractmethod
  def register_page(self):
      pass

  @abstractmethod
  def login(self, **kwargs):
      pass

  @abstractmethod
  def is_logged(self):
      pass

  def start(self) -> None:
      Client.playwright = sync_playwright().start()
      Client.browser = Client.playwright.chromium.launch(**BROWSER_CONFIG)
      if self.storage_state_file_path is not None and os.path.isfile(self.storage_state_file_path):
          self.context = Client.browser.new_context(**CONTEXT_CONFIG, storage_state=self.storage_state_file_path)
      else:
          self.context = Client.browser.new_context(**CONTEXT_CONFIG)
      self.main_page = self.context.new_page()
      self.main_page.goto(self.url)

      self.register_page()

  def close(self) -> None:
      if self.storage_state_file_path is not None:
          if not os.path.exists('storage_state'):
              os.mkdir('storage_state')
          self.context.storage_state(path=self.storage_state_file_path)
      self.main_page.close()
      self.context.close()

def logged_user(client, url, *, scope = 'function', storage_state_name = None, **kwargs):
  @pytest.fixture(scope=scope)
  def user_fixture():
      user = client(url, storage_state_name=storage_state_name)
      user.start()
      logger.info(f'用户登录: {kwargs}')
      if not user.is_logged():
          user.login(**kwargs)
      yield user
      user.close()

继承Client类,使用 logger_user 函数来生成不同终端用户的fixture,以实现继承Pytest。

关于Playwright中如何保持登录状态的文章就介绍至此,更多相关Playwright保持登录状态内容请搜索编程宝库以前的文章,希望以后支持编程宝库

最近组内推行Playwright进行自动化测试,由我封装了一个简单的框架供大家使用。本意是想让大家使用Page Object模式来编写自动化代码的,可是不知道是因为大家嫌麻烦,还是Playw ...