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

王子夜 · July 30, 2020 · Last by wskuku replied at August 03, 2020 · 1866 hits

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 的。

小酷 回复

元素定位是对的,

你的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是不能去定义变量类型的

8Floor has been deleted

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)?

王子夜 #14 · July 30, 2020 作者

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

陈恒捷 回复

恒捷, 大神, 我想再问一下,我试了运行我的程序, 我再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

王子夜 #20 · July 31, 2020 作者
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 回复

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

Author only
Author only
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up