Selenium python-unittest 框架利用 PageObject 设计模式在 BasePage 类中调用不到 send_keys 方法,运行报错'NoneType' object has no attribute 'send_keys'

王子夜 · 2020年07月30日 · 最后由 wskuku 回复于 2020年08月03日 · 4282 次阅读

BasePage 类代码如下:
from selenium.webdriver.support.wait import WebDriverWait as WD
from selenium.webdriver.support import expected_conditions as EC

class BasePage(object):

def init(self, driver):
self.driver = driver

def find_element(self, *loc):
try:
WD(self.driver, 60).until(EC.visibility_of_element_located(*loc))
return self.find_element(*loc)
except:
print("元素没找到")

def send_keys(self, value, *loc):
self.find_element(*loc).send_keys(value)

第一个问题:上面的 BasePage 类中的 driver 是从 TestCase 类中传过来的,但是我在写 def send_keys(self, value, *loc) 这个方法的时候,写 self.find_element(*loc).send_keys(value) 的时候,send_keys(value) 这个方法是调用不出来的,是不是说明我的 driver 没有传递过来?如下图:

HomePage 类继承了 BasePage 类,代码如下:

from basepage import BasePage
from selenium.webdriver.common.by import By

class HomePage(BasePage):

search_input = (By.XPATH, "//div[@id='home-search']//input[@class='typeahead form-control']")
search_button = (By.XPATH, "//div[@id='home-search']//span[@class='icon-search']")

def input_search(self, value):
self.send_keys(value, self.search_input)

def click_search_button(self):
self.find_element(self.search_button).click()

TestCase 类的代码如下:

import unittest
from selenium import webdriver
from homepage import HomePage

class TestSearch(unittest.TestCase):

@classmethod
def setUpClass(cls):
cls.driver = webdriver.Chrome()
cls.homepage = HomePage(cls.driver)
cls.driver.get("https://support.lenovo.com/us/en")

def test_search(self):
self.homepage.input_search("lenovo")
self.homepage.click_search_button()

@classmethod
def tearDownClass(cls):
cls.driver.quit()

if name == 'main':
unittest.main()

上面的 TestCase 类中 driver 通过 cls.homepage = HomePage(cls.driver) 传递给 homepage 类,然后传递给 BasePage 类中的 driver,但是 BasePage 类中的 driver 却调用不到 selenium webdriver 的一些常用方法,比如 send_keys(), page_souce() 等,是 driver 没有传递到 BasePage 类中吗?

然后我运行我的 TestCase, 果然在这里 self.find_element(*loc).send_keys(value),报错了,错误如下:

File "D:\automationTestProject\johnunittest\basepage.py", line 20, in send_keys
return self.find_element(*loc).send_keys(value)
AttributeError: 'NoneType' object has no attribute 'send_keys'

小弟以前是学 JAVA 的,刚刚学习 python 自动化,请各位大神,指点一下,非常感谢!

最佳回复
子夜 回复

都是面向对象,我理解两个语言在类和方法方面差异没那么大。

你说不能点出来,指的应该是 ide 的自动补全吧。python 是弱类型语言,所以不会要求声明时明确对象类型。java 是强类型语言,这个是一个差异。

但强弱类型的差异只是写法上的差异以及编译器是否能直接判断而已。不管哪个语言,你那个对象实际类型需要是 driver 这个是没有变的。pycharm 这类 ide 也很聪明,会通过你的代码去推断对象类型,进而给出对应的提示。你没给出提示,说明 ide 没能推断出类型(比如你用反射赋值的类型,就无法通过代码直接推断,得运行时才知道)。

PS:我看到一个没太理解的地方。你最上面的 BasePage 类,声明了 self.driver = driver 后,没见到哪里用到这个 self.driver 呀,HomePage 类也是一样。self 和 self.driver 是两个完全不同的对象。python 的 self 约等价于 java 的 this 。你写 java 也应该是 this.driver.findElement(xx) 吧?

testcase 里也用了 cls.driver.get("https://support.lenovo.com/us/en") 来调用 driver 子方法,为啥 page 类里就不这么用了呢?

共收到 23 条回复 时间 点赞

请各位 python 自动化大神,帮助小弟一下,在这里卡住了,头疼奥

用 markdown 整理一下格式吧,这代码看得好累

没定位到元素

AttributeError: 'NoneType' object has no attribute 'send_keys' ,说明你的 self.find_element(*loc) 返回 None ,等价于 Java 返回了 null 。
然后你就可以类比 java 的解决方法,看下为何这个找元素返回的是 None 了。一般来说,找元素找不到,应该会抛异常而不是直接返回 None 的。

cool 回复

元素定位是对的,

你的 find_element 方法里,返回一个 find_element,比较明显的递归,如果元素一直可以找到,那你会在递归里无限循环,如果元素找不到,你的 except 里没有返回内容,所以会是一个 NoneType。

陈恒捷 回复

恩,我就是对照 JAVA, 但是 JAVA 和 Python 真的有些不同,Python 只能在 testcase 中实例化这个 driver = webriver.Chrome(). 然后通过 page 类传递这个 driver 到 basepage 类中,但是在 basepage 类中我去通过传过来的 driver 去调用 webdriver 的内部方法时候,却点不出来这些内部方法 (webdriver.send_keys(), webriver.page_source() 等). 而 JAVA 可以在 basepage 类中定义变量类型是 webdriver,这样在 basepage 类就可以,调用出 webdriver 的内部方法. python 是不能去定义变量类型的

23楼 已删除

HomePage 类没有继承父类的init方法 无法找到 driver

在 HomePage 类中增加代码

def __init__(self, driver):
        super(HomePage, self).__init__(driver)

----Good Luck-----

应该是 self.driver.find_element() 吧;
self.driver 没见你用;

def find_element(self, *loc):
try:
WD(self.driver, 60).until(EC.visibility_of_element_located(*loc))
return self.find_element(*loc)
except:
print("元素没找到")

这是递归方法,根本不返回任何对象,应该用 self.driver.find_element(*loc) 或者方法名称改一下,网上的示例很多都是错
还有传参也写错了,要这样写

def input_search(self, value):
self.send_keys(value, self.search_input[0],self.search_input[1])

def click_search_button(self):
self.find_element(self.search_input[0],self.search_input[1]).click()

子夜 回复

都是面向对象,我理解两个语言在类和方法方面差异没那么大。

你说不能点出来,指的应该是 ide 的自动补全吧。python 是弱类型语言,所以不会要求声明时明确对象类型。java 是强类型语言,这个是一个差异。

但强弱类型的差异只是写法上的差异以及编译器是否能直接判断而已。不管哪个语言,你那个对象实际类型需要是 driver 这个是没有变的。pycharm 这类 ide 也很聪明,会通过你的代码去推断对象类型,进而给出对应的提示。你没给出提示,说明 ide 没能推断出类型(比如你用反射赋值的类型,就无法通过代码直接推断,得运行时才知道)。

PS:我看到一个没太理解的地方。你最上面的 BasePage 类,声明了 self.driver = driver 后,没见到哪里用到这个 self.driver 呀,HomePage 类也是一样。self 和 self.driver 是两个完全不同的对象。python 的 self 约等价于 java 的 this 。你写 java 也应该是 this.driver.findElement(xx) 吧?

testcase 里也用了 cls.driver.get("https://support.lenovo.com/us/en") 来调用 driver 子方法,为啥 page 类里就不这么用了呢?

难道不是 self.driver.find_element(*loc).send_keys(value)?

非常感谢各位,我试了各位的建议,我的问题已经解决了,

陈恒捷 回复

恒捷, 大神, 我想再问一下,我试了运行我的程序, 我再 BasePage 类中从外部传递过来的 driver, 是可以执行我的 webdriver 内部方法的,比如 send_keys(), driver.page_source() 等, 这说明我是在运行时才知道去调用那些方法.
但是我如果想在写代码的时候就能推断出我的这个 driver 就是 webdriver, 从而在 BasePage 类中我可以直接通过 element1 = self.driver.find_element(*loc). element1 点出 send_keys() 这个方法, (目前我写的 send_keys() 是我手动输入的), 我应该怎么写? 还需要在我的 basepage 类中再实例化我的 webdriver 吗?

王子夜 回复

楼主是咋解决的呢 吸取下经验啊

GoodLuck 回复

没写那么复杂, 就下面这样:

def find_element(self, *loc):
try:
element = WD(self.driver, 60).until(EC.visibility_of_element_located(*loc))
return element
except:
print("元素没找到")
子夜 回复

声明 driver 参数的类型即可,如
def demo (driver: webdriver.Chrome()):
pss

Midming 回复

不行呀 ,这样还是不能获取到 webdriver 的内部方法, 点不出 webdriver.get() 方法。 代码如下:

class BasePage(object):

    def __init__(self, driver: webdriver.Chrome()):
        self.driver = driver
    def open_url(self, url):
        self.driver.
王子夜 回复

sorry,把()去掉,即 driver: webdriver.Chrome

Midming 回复

这下对了,给你个大大的赞,哈哈

仅楼主可见
仅楼主可见
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册