# Java自动化测试面试题
🔗测试开发训练营 (opens new window):针对面试求职的测试开发训练营,大厂导师 1v1 私教,帮助学员从 0 到 1 拿到测开满意的 offer
- ✅直播授课+实时互动答疑,课程到就业贯穿企业级案例,由测试开发导师全程主讲,绝无其他助理老师代课。
- ✅大厂测试开发导师一对一求职陪跑,覆盖课程答疑+简历打磨+面试模拟面试+面试复盘等求职辅导
# 一、接口自动化(高优)
# 1. Java接口自动化框架是如何搭建的?用了哪些技术栈?
答案话术: 我们的Java接口自动化框架主要使用RestAssured+TestNG+Maven搭建。RestAssured用来发起HTTP接口请求,支持各种请求方式;TestNG作为测试框架管理用例,支持用例分组、依赖、并发执行;Maven做依赖管理和构建工具;Jackson用来处理JSON数据的序列化和反序列化;Allure生成可视化测试报告;Log4j2打印执行日志;MyBatis操作MySQL数据库做数据校验。整个框架采用分层设计,包括测试用例层、业务封装层、工具类层和配置层,代码结构清晰,便于维护。
# 2. Java接口自动化相比Python有什么优势?
答案话术: 主要有三个优势:第一是类型安全,Java是强类型语言,编译期就能发现类型错误,代码更健壮;第二是性能更好,Java执行效率比Python高,适合大规模并发测试;第三是企业级支持更成熟,很多测试平台和CI/CD工具都是Java技术栈,集成更方便。我们电商项目有上百个接口,用Java框架执行500条用例只需5分钟,稳定性也很好。
# 3. RestAssured和HttpClient有什么区别?为什么选RestAssured?
答案话术: HttpClient是Apache的HTTP客户端库,功能强大但API复杂,需要写很多代码;RestAssured专门为REST API测试设计,API简洁,支持BDD风格,可读性强。比如发送一个POST请求,HttpClient要写10多行代码,RestAssured只需要3-4行。而且RestAssured内置JSON/XML解析、请求响应日志、断言等功能,开箱即用。所以我们选择RestAssured,开发效率更高。
# 4. TestNG和JUnit的区别是什么?为什么用TestNG?
答案话术: 主要区别有三点:第一,TestNG支持更灵活的用例组织,可以用groups分组、dependsOnMethods设置依赖;第二,TestNG的注解更丰富,比如@DataProvider做数据驱动,@Parameters传参;第三,TestNG支持并发执行和失败重试。我们电商项目用例很多,用TestNG的groups把下单、支付、退款等不同模块分组,可以灵活执行,而且失败重试机制能排除环境干扰,所以选择TestNG。
# 5. 如何实现接口关联?从订单接口提取订单号传给支付接口?
答案话术:
我们用JsonPath提取响应数据。比如下单接口返回订单号,我用JsonPath表达式$.data.orderId提取出来,存到全局变量或者类的静态变量里,然后支付接口直接引用这个变量。如果接口关联比较复杂,我会封装一个Context类统一管理关联数据,每个接口执行完把需要的数据put进去,下个接口get出来使用,这样代码更清晰。
# 6. 数据驱动怎么实现?用Excel还是代码?
答案话术: 我们主要用TestNG的@DataProvider注解实现数据驱动。对于简单场景,直接在代码里返回Object[][]数组;对于复杂场景,我们用POI库读取Excel文件,把每行数据封装成JavaBean对象,然后用@DataProvider返回。这种方式灵活性高,测试数据和代码分离,业务人员也能维护Excel数据。我们电商的商品查询接口,就是用Excel管理几十组测试数据,一个测试方法跑完所有场景。
# 7. 接口鉴权如何处理?Token怎么管理?
答案话术:
我们电商项目用JWT Token鉴权。首先调用登录接口获取Token,用JsonPath提取出来存到全局变量,然后其他接口的请求头统一添加Authorization: Bearer {token}。我封装了一个BaseTest类,在@BeforeClass里获取Token并设置到RequestSpecification中,所有测试类继承这个类就自动带上Token了。Token有过期时间,我还加了失效自动刷新的逻辑,确保用例稳定执行。
# 8. 如何做接口Mock测试?
答案话术: 我们用WireMock做接口Mock。比如下单接口依赖库存服务,但库存服务还没开发完,我就用WireMock在本地启动一个Mock服务器,配置规则返回固定的库存数据。这样可以提前测试下单逻辑,不被依赖阻塞。Mock还能模拟各种异常场景,比如库存不足、服务超时、返回错误码等,覆盖更全面。
# 9. 断言怎么写?用什么工具?
答案话术:
我们主要用Hamcrest和AssertJ两个断言库。Hamcrest是TestNG推荐的,语法简洁,比如assertThat(response.statusCode(), equalTo(200));AssertJ更强大,支持链式调用和丰富的断言,比如assertThat(order.getStatus()).isNotNull().isEqualTo("PAID")。我一般会组合使用,对状态码、响应字段、数据库数据都做断言,确保接口返回正确。
# 10. 如何处理接口加密和签名?
答案话术: 我们电商的支付接口需要MD5签名。我封装了一个SignUtils工具类,把请求参数按字典序排列,拼接后加密钥做MD5,生成签名字段。然后在发送请求前调用这个工具类,自动给请求体加上sign字段。响应解密也类似,我用Base64或AES解密工具类处理返回数据。这样业务代码不用关心加解密细节,保持简洁。
# 11. 并发测试怎么做?
答案话术:
TestNG支持用例并发执行,我在testng.xml里配置parallel="methods" thread-count="10",就能10个线程并发跑测试方法。我们用这个测试秒杀接口,模拟100个用户同时下单,验证库存扣减是否准确、有没有超卖问题。还会结合数据库查询,确认最终库存数量正确。这种并发测试能暴露线程安全问题。
# 12. 测试数据如何准备和清理?
答案话术: 我用@BeforeMethod准备数据,@AfterMethod清理数据。比如测试订单查询接口,我在@BeforeMethod里调用下单接口创建测试订单,拿到订单号;测试完成后在@AfterMethod里调用取消订单接口或者直接删除数据库记录,保证数据干净。对于复杂场景,我还会用MyBatis直接操作数据库批量插入测试数据,速度更快。
# 13. 如何统计代码覆盖率?
答案话术: 我们用JaCoCo统计代码覆盖率。在Maven的pom.xml里配置JaCoCo插件,跑完测试后生成jacoco.exec文件,然后用Ant或Maven命令生成HTML报告。报告能看到每个类、每个方法的覆盖率,哪些代码没被测试覆盖。我们要求核心接口代码覆盖率达到80%以上,通过覆盖率报告针对性补充用例,实现精准测试。
# 14. 持续集成怎么做?
答案话术:
我们用Jenkins实现CI/CD。在Jenkins里配置Maven项目,设置Git地址,添加构建触发器,比如代码提交自动构建或者每天晚上定时构建。构建步骤执行mvn clean test,跑完自动化测试后,用Allure插件生成测试报告,再配置邮件通知把报告发给相关人员。整个流程全自动,开发提交代码后就能及时发现问题。
# 15. 如何提高框架的稳定性?
答案话术: 主要三个方面:第一是增加失败重试,TestNG有@RetryAnalyzer注解,失败的用例自动重跑3次,排除环境不稳定的干扰;第二是加等待机制,接口响应慢时用Thread.sleep或者轮询等待,避免超时;第三是异常处理,用try-catch捕获异常,记录详细日志,用例失败也能继续执行不影响其他用例。我们框架经过这些优化,稳定性提升很多。
# 16. 性能测试数据怎么准备?
答案话术: 我用三种方式:第一是从数据库捞现有数据,导出成CSV或JSON;第二是写自动化脚本调用接口批量造数据,比如调注册接口创建1000个用户;第三是用MyBatis写SQL批量插入数据。电商项目商品数据我直接写SQL插入,用户数据用自动化接口创建,订单数据用JMeter参数化CSV文件,组合使用效率最高。
# 17. 遇到过哪些技术难点?
答案话术: 最难的是接口依赖复杂的场景。比如下单流程,要先查商品库存,再创建订单,再调用支付接口,最后扣减库存更新订单状态,涉及4-5个接口的串联。我通过封装业务流程类,把这些接口按顺序封装成一个方法,传入商品ID和用户ID就能完成整个下单,上层测试用例调用起来很简单。还有就是环境不稳定导致用例失败,我加了重试和详细日志,问题基本解决了。
# 18. 为什么选Java而不是Python做接口自动化?
答案话术: 主要三个原因:第一,我们后端是Java技术栈,测试用Java能更好地理解代码逻辑,方便和开发沟通;第二,Java执行效率高,500条用例跑5分钟,Python要10分钟;第三,公司有Java测试平台和工具,集成方便。虽然Python上手快,但综合考虑Java更适合我们项目。
# 19. 如何做接口性能测试?
答案话术: 虽然我们主要用JMeter做性能测试,但Java接口自动化框架也能做简单的性能测试。我用多线程模拟并发,CountDownLatch控制线程同时发起请求,记录响应时间和成功率。比如测试秒杀接口,我启动100个线程同时下单,统计TPS和平均响应时间,验证接口性能是否达标。这种方式适合快速验证,深度性能测试还是用专业工具。
# 20. 框架还有哪些可以优化的地方?
答案话术: 主要三个方向:第一是用例执行速度,目前500条用例5分钟,可以优化成3分钟,通过增加并发度和减少不必要的等待;第二是失败分析,现在失败需要人工看日志,可以做智能分析,自动分类失败原因;第三是测试数据管理,现在数据分散在代码和Excel里,可以统一到数据库或者配置中心,更好管理。这些优化能让框架更高效易用。
# 二、Web自动化(中等)
# 1. Java Web自动化框架是如何搭建的?用了哪些技术?
答案话术: 我们使用Selenium+TestNG+Maven搭建Web自动化框架。Selenium WebDriver负责浏览器操作,TestNG管理测试用例和断言,Maven做依赖管理和构建。还集成了PageFactory做页面对象模型,ExtentReports生成测试报告,Log4j2记录日志。整个框架采用POM设计模式,把页面元素和业务操作分离,一个页面对应一个Page类,测试用例只调用Page类的方法,代码可维护性很强。
# 2. 什么是POM(页面对象模型)?为什么要用POM?
答案话术:
POM就是把每个页面的元素定位和操作封装成一个Page类。比如登录页面封装成LoginPage类,里面定义用户名输入框、密码输入框、登录按钮的定位,以及输入用户名、输入密码、点击登录的方法。测试用例只需要调用loginPage.login(username, password),不用关心元素怎么定位。好处是当页面元素变化时,只需要修改Page类,测试用例不用动,维护成本大大降低。我们电商项目有20多个Page类,结构非常清晰。
# 3. 如何处理元素定位?优先使用哪种定位方式?
答案话术: 元素定位我优先使用ID,因为ID是唯一的,定位速度快且稳定。如果没有ID,我会用name或者class。对于复杂场景用XPath或CSS Selector,XPath功能强大但性能稍差,CSS Selector性能好但功能有限,我会根据实际情况选择。我们电商项目商品列表用XPath定位第几个商品,购物车用CSS Selector定位价格元素。另外我会尽量避免用文本定位,因为文本容易变化。
# 4. 如何处理动态元素?元素加载慢怎么办?
答案话术:
我主要用三种等待方式:第一是显式等待WebDriverWait,等待特定元素出现或可点击,比如WebDriverWait.until(ExpectedConditions.elementToBeClickable(element));第二是隐式等待implicitlyWait,设置全局等待时间;第三是强制等待Thread.sleep,但这个会拖慢执行速度,我尽量少用。我们电商的搜索结果页面,商品列表是Ajax加载的,我用显式等待,等待第一个商品元素出现再进行后续操作,非常稳定。
# 5. 如何处理弹窗、Alert、iframe?
答案话术:
Alert弹窗用driver.switchTo().alert()切换,然后accept()接受或dismiss()取消。iframe用driver.switchTo().frame()切换进去操作,操作完用driver.switchTo().defaultContent()切回主页面。新窗口用driver.switchTo().window(windowHandle)切换。我们电商支付页面是iframe嵌入的,我就先切换到iframe,输入支付密码,点击确认,然后切回主页面验证订单状态,这些操作都封装在PaymentPage类里。
# 6. 浏览器兼容性测试怎么做?
答案话术:
我用TestNG的@Parameters注解传入浏览器类型,在@BeforeClass里根据参数初始化不同的WebDriver。在testng.xml里配置多个test节点,分别传入Chrome、Firefox、Edge参数,这样一套用例就能在多个浏览器上跑。我们电商项目主要测试Chrome和Safari,因为用户量最大。另外还会测试不同分辨率,用driver.manage().window().setSize()设置窗口大小,验证响应式布局。
# 7. 如何截图?什么时候截图?
答案话术:
我用TakesScreenshot接口截图,代码是File screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE),然后用FileUtils保存到指定路径。我在两个地方截图:一是用例失败时,在@AfterMethod里判断如果失败就截图,方便定位问题;二是关键步骤,比如下单成功、支付完成,截图留证。截图文件名我用测试方法名_时间戳.png命名,并且把截图路径写到Allure报告里,查看报告时能直接看到截图。
# 8. 数据驱动怎么实现?
答案话术: 我用TestNG的@DataProvider注解实现数据驱动。对于简单场景,直接在代码里返回二维数组;对于复杂场景,用POI读取Excel,把每行数据转成对象返回。比如测试商品搜索功能,我在Excel里维护不同的搜索关键词和期望结果,@DataProvider读取后传给测试方法,一个方法就能跑完所有场景。这样测试数据和代码分离,业务人员也能维护Excel,非常灵活。
# 9. 如何处理验证码?
答案话术: 验证码主要三种处理方式:第一是让开发提供测试环境的万能验证码,比如固定输入"1234"就能通过;第二是开发提供跳过验证码的后门,测试账号不校验验证码;第三是用OCR识别,但准确率不高我们很少用。我们电商项目测试环境用的是万能验证码方式,在配置文件里配置验证码值,代码里直接读取填入,简单稳定。生产环境测试我们会手工介入输入验证码。
# 10. 如何做断言?断言失败后是否继续执行?
答案话术:
我主要用TestNG的Assert类做断言,比如Assert.assertEquals(actual, expected)。默认断言失败后会抛异常,后面的代码不执行。如果我想多个断言失败后继续执行,我用SoftAssert,它会收集所有失败的断言,最后调用assertAll()统一抛出。比如验证订单详情页,我会断言订单号、商品名称、价格、状态等多个字段,用SoftAssert可以一次看到所有失败的断言,定位问题更快。
# 11. PageFactory和普通Page类有什么区别?
答案话术:
PageFactory是Selenium提供的页面工厂模式。普通Page类用driver.findElement(By.id("username"))查找元素,每次都要写定位代码;PageFactory用@FindBy(id="username")注解直接声明元素,在构造函数里调用PageFactory.initElements(driver, this)初始化,代码更简洁。而且PageFactory支持懒加载,元素只在使用时才查找,性能更好。我们电商项目统一用PageFactory,代码量少了30%。
# 12. 如何执行JavaScript代码?
答案话术:
我用JavascriptExecutor接口执行JS代码。常见场景有三个:一是滚动页面,js.executeScript("window.scrollTo(0, document.body.scrollHeight)")滚动到底部,触发懒加载;二是操作隐藏元素,有些元素Selenium点不了,用JS直接click;三是获取元素属性,比如js.executeScript("return arguments[0].value", element)获取input的value。我们电商的商品列表是无限滚动的,我就用JS滚动到底部加载更多商品。
# 13. 如何处理文件上传和下载?
答案话术: 文件上传有两种方式:一是如果是input标签type="file",直接用sendKeys()传入文件路径;二是如果是自定义上传按钮,需要用AutoIt或Robot类模拟键盘操作,但这种方式不推荐因为不稳定。文件下载我一般不验证下载过程,而是验证下载后的文件,用Java的File类检查下载目录是否有对应文件,或者验证文件大小、内容是否正确。我们电商的订单导出功能,我就验证导出的Excel文件是否存在且有数据。
# 14. 多窗口、多Tab页怎么处理?
答案话术:
我用driver.getWindowHandles()获取所有窗口句柄,然后用driver.switchTo().window(handle)切换。一般我会在打开新窗口前先保存原窗口句柄,操作完新窗口后切回原窗口。比如商品详情页点击查看物流,会新开一个Tab页,我就获取新Tab的handle,切过去验证物流信息,然后关闭这个Tab,切回商品详情页。我封装了一个WindowUtils工具类统一管理窗口切换,代码更清晰。
# 15. 如何处理富文本编辑器?
答案话术:
富文本编辑器一般是iframe或者contenteditable的div。如果是iframe,我先switchTo().frame()切换进去,然后用sendKeys()输入内容;如果是div,我用JavascriptExecutor执行arguments[0].innerHTML='content'直接设置内容,因为sendKeys()有时候不稳定。我们电商的商品评价是富文本编辑器,我就用JS直接设置innerHTML,然后提交评价,验证评价内容是否正确显示。
# 16. 测试数据如何管理?
答案话术:
我们把测试数据分三类管理:第一类是配置信息,比如URL、账号密码,放在properties文件里,用ResourceBundle读取;第二类是业务数据,比如商品信息、用户信息,放在Excel或JSON文件里,用POI或Jackson读取;第三类是数据库数据,直接用JDBC或MyBatis操作数据库。我封装了一个DataUtils工具类统一读取,测试用例只需要调用DataUtils.getTestData("login")就能获取登录数据,非常方便。
# 17. 失败重试机制怎么实现?
答案话术:
我实现了一个RetryAnalyzer类,继承IRetryAnalyzer接口,在retry方法里判断如果失败且重试次数小于3次就返回true,TestNG会自动重跑失败的用例。然后在testng.xml里配置<listeners><listener class-name="com.test.RetryAnalyzer"/></listeners>全局生效,或者在测试方法上加@Test(retryAnalyzer = RetryAnalyzer.class)单独配置。这样能排除环境不稳定导致的偶发失败,提高用例稳定性。
# 18. 如何生成测试报告?
答案话术:
我用Allure生成测试报告。首先在pom.xml里添加allure-testng依赖,然后在代码里用@Step注解标记测试步骤,用@Attachment添加截图附件。跑完测试后,执行allure generate命令生成HTML报告。报告很详细,可以看到每个用例的执行步骤、截图、日志、执行时间,还有饼图、趋势图等统计信息。我把报告集成到Jenkins,测试完成后自动生成并发送邮件链接给团队。
# 19. 持续集成怎么做?
答案话术:
我们用Jenkins实现CI/CD。在Jenkins配置Maven项目,关联Git仓库,设置触发器,比如代码提交自动构建或每晚定时构建。构建步骤执行mvn clean test -Dbrowser=chrome,指定浏览器运行测试。跑完后用Allure插件生成报告,配置邮件通知发送给相关人员。我们还配置了测试失败超过10%就不允许发布,确保代码质量。整个流程全自动,大大提升了测试效率。
# 20. 框架还有哪些可以优化的地方?
答案话术: 主要三个方向:第一是执行速度,现在200条用例跑30分钟,可以用Selenium Grid做分布式并行执行,缩短到10分钟;第二是稳定性,虽然有重试机制,但偶尔还是会失败,可以优化等待策略,增加健壮性;第三是维护成本,现在Page类有点多,可以抽象一些公共组件,比如Header、Footer、Pagination,减少重复代码。这些优化能让框架更高效可靠。
# 三、App自动化(低优)
# 1. 你们公司App自动化是怎么做的?用什么框架?
答案话术: 我们使用Java+Appium+TestNG搭建App自动化框架。
框架分层设计:最上层是TestCase测试用例层,使用TestNG管理用例;第二层是PageObject页面对象层,封装每个页面的元素和操作方法;第三层是Driver驱动层,封装Appium启动和基础操作;底层是Utils工具类,包含等待、截图、日志等公共方法。
主要覆盖电商App的核心流程:商品搜索、加购、下单、支付等关键业务场景。总共编写200多条自动化用例,通过Jenkins实现每日自动执行,大大提升了回归测试效率。
# 2. 为什么选择Appium而不是其他框架?
答案话术: 选择Appium主要有三个原因:
第一是跨平台,一套代码可以同时测试Android和iOS,不需要分别维护两套脚本,降低了维护成本。
第二是支持多语言,我们团队用Java开发,Appium支持Java、Python等多种语言,不需要额外学习成本。
第三是开源社区活跃,遇到问题容易找到解决方案,文档和教程也比较丰富。
相比UIAutomator只支持Android、Espresso需要修改源码,Appium更适合我们的实际需求。
# 3. Appium的工作原理是什么?
答案话术: Appium采用客户端-服务器架构。
工作流程是:首先测试脚本通过WebDriver协议发送HTTP请求给Appium Server;Appium Server接收请求后,根据平台类型(Android或iOS)调用相应的驱动,Android用UIAutomator2,iOS用XCUITest;然后驱动将指令翻译成手机能理解的命令,在真机或模拟器上执行操作;最后将执行结果返回给Appium Server,再返回给测试脚本。
整个过程不需要修改App源码,通过系统自带的测试框架来驱动应用。
# 4. 如何定位App元素?常用哪些定位方式?
答案话术: 我常用的定位方式有五种:
第一是id定位,这是最稳定的方式,比如driver.findElement(By.id("com.app:id/search"))。
第二是xpath定位,比较灵活,适合复杂场景,比如//android.widget.TextView[@text='立即购买']。
第三是className定位,适合批量获取同类型元素。
第四是accessibility id定位,通过content-desc属性定位,对无障碍功能友好。
第五是uiautomator定位,这是Android特有的,功能强大,比如new UiSelector().text("加入购物车")。
实际工作中,我优先用id,其次用accessibility id,复杂场景才用xpath,因为xpath性能相对较差。
# 5. 如何获取App元素的定位信息?
答案话术: 主要用两个工具:
Android端用uiautomatorviewer,这是Android SDK自带的工具。先打开App到目标页面,然后运行uiautomatorviewer,点击左上角截图按钮,就能看到页面层级和每个元素的resource-id、class、text等属性。
iOS端用Appium Desktop的Inspector功能,连接手机后可以实时查看页面结构和元素属性。
另外也可以直接在代码里打印driver.getPageSource()来查看页面结构,不过这种方式不太直观。
# 6. 遇到元素定位不到怎么办?
答案话术: 我一般从三个方面排查:
第一是检查定位表达式是否正确,用uiautomatorviewer确认元素属性是否变化,特别是动态id的情况。
第二是加显式等待,因为元素可能还没加载出来,我会用WebDriverWait配合ExpectedConditions.presenceOfElementLocated等待元素出现。
第三是检查是否在WebView里,如果是H5页面需要先切换context,driver.context("WEBVIEW_com.app"),然后再定位。
还有就是检查元素是否被其他元素遮挡,或者在屏幕可见范围之外需要先滑动。
# 7. 如何处理Toast提示框?
答案话术: Toast提示框比较特殊,它不是标准控件,定位方式有两种:
第一种是用xpath定位,Toast的class是android.widget.Toast,可以这样写:
driver.findElement(By.xpath("//*[@class='android.widget.Toast']")).getText()
第二种是用uiautomator定位,这个更稳定:
driver.findElement(By.xpath("//*[contains(@text,'添加成功')]"))
因为Toast显示时间短,所以要设置较短的隐式等待时间,或者用显式等待配合短超时时间。我一般设置3秒超时,如果定位不到就认为Toast没出现。
# 8. 如何实现App的滑动操作?
答案话术: 滑动操作主要有两种方式:
第一种是用TouchAction类,适合简单的上下左右滑动:
TouchAction action = new TouchAction(driver);
action.press(PointOption.point(500, 1500))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point(500, 500))
.release().perform();
第二种是用Android的uiautomator命令,比如滑动查找元素:
driver.findElement(MobileBy.AndroidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text(\"商品详情\"))"
));
实际项目中,我一般封装成公共方法,传入滑动方向和次数,方便复用。
# 9. PageObject模式是什么?为什么要用它?
答案话术: PageObject是页面对象模式,把每个页面封装成一个类,页面元素作为类的属性,页面操作作为类的方法。
优点主要有三个:
第一是提高可维护性,当页面元素变化时,只需要修改对应的Page类,不用改所有用例。
第二是提高复用性,多个用例可以调用同一个Page类的方法,避免代码重复。
第三是提高可读性,用例代码变得更清晰,比如loginPage.login("用户名", "密码")比直接写一堆findElement要容易理解。
我们项目中把首页、商品列表页、详情页、购物车页、订单页都封装成了Page类,用例只需要调用相应的方法就行。
# 10. 如何处理App的权限弹窗?
答案话术: 权限弹窗主要有两种处理方式:
第一种是在代码里自动点击允许,定位"允许"或"始终允许"按钮并点击:
try {
driver.findElement(By.id("com.android.packageinstaller:id/permission_allow_button")).click();
} catch (Exception e) {
// 弹窗不存在,继续执行
}
第二种是用Appium的自动授权功能,在初始化driver时设置capabilities:
capabilities.setCapability("autoGrantPermissions", true);
这样App安装后会自动授予所有权限,不会弹窗。
我们项目用的是第二种,更简单可靠。如果是测试权限拒绝场景,才会手动点击拒绝按钮。
# 11. 如何实现多设备并行测试?
答案话术: 多设备并行主要通过TestNG的并行功能实现。
首先在testng.xml配置并行模式:
<suite name="Suite" parallel="tests" thread-count="3">
然后在代码里根据不同设备初始化不同的driver,用ThreadLocal保证线程安全:
private static ThreadLocal<AndroidDriver> driver = new ThreadLocal<>();
启动时传入不同设备的udid和端口号,让每个线程连接不同设备。
我们公司有3台测试机,配置好后可以同时在3台设备上跑用例,测试时间从1小时缩短到20分钟。
# 12. 如何处理WebView混合页面?
答案话术: WebView是H5页面嵌入在App里,需要切换context才能定位。
处理步骤是:
第一步,获取所有context:
Set<String> contexts = driver.getContextHandles();
第二步,切换到WEBVIEW:
for (String context : contexts) {
if (context.contains("WEBVIEW")) {
driver.context(context);
break;
}
}
第三步,像操作网页一样定位元素,可以用id、name、xpath等。
第四步,操作完成后切回原生:
driver.context("NATIVE_APP");
我们电商项目的商品详情页就是H5,测试时需要这样切换。
# 13. 如何实现截图功能?
答案话术: 截图主要用于失败时保存现场,便于问题定位。
我封装了一个截图方法:
public void takeScreenshot(String name) {
File srcFile = driver.getScreenshotAs(OutputType.FILE);
String path = "./screenshots/" + name + "_" + System.currentTimeMillis() + ".png";
FileUtils.copyFile(srcFile, new File(path));
}
然后在TestNG的失败监听器里调用:
@Override
public void onTestFailure(ITestResult result) {
takeScreenshot(result.getName());
}
这样每次用例失败都会自动截图,截图会保存在screenshots目录下,文件名包含用例名和时间戳。
# 14. 如何生成测试报告?
答案话术: 我们主要用两种方式生成报告:
第一种是TestNG自带的报告,执行完自动生成test-output目录,里面有index.html,包含用例执行情况、通过率、失败原因等。
第二种是Allure报告,更美观和详细。在pom.xml添加Allure依赖,用例上加@Description等注解,执行完后运行allure generate生成报告,支持失败截图、执行趋势、用例分类等功能。
报告生成后,通过Jenkins自动发送邮件给相关人员,邮件里包含测试概况和报告链接。
# 15. 如何处理App启动慢的问题?
答案话术: App启动慢会导致元素定位超时,我的处理方法有三个:
第一是设置合理的隐式等待时间,给App足够的启动时间:
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
第二是在启动后加一个显式等待,等待首页关键元素出现:
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("首页元素id")));
第三是用Appium的capability控制启动参数:
capabilities.setCapability("appWaitActivity", "主Activity");
capabilities.setCapability("appWaitDuration", 30000);
这样Appium会等待指定Activity出现才认为启动完成。
# 16. 自动化框架是如何分层的?
答案话术: 我们的框架采用分层设计,分为五层:
最上层是TestCase测试用例层,用TestNG管理用例,使用@Test注解,调用Page层方法完成业务流程测试。
第二层是PageObject页面对象层,每个页面对应一个Page类,封装页面元素定位和操作方法。
第三层是Driver驱动层,封装Appium启动、关闭和基础操作,提供统一的driver实例。
第四层是Utils工具类层,包含等待、滑动、截图、日志、配置读取等公共方法。
底层是Config配置层,用properties文件存储设备信息、App路径、服务器地址等配置,方便切换环境。
这种分层结构职责清晰,修改某一层不影响其他层,便于维护和扩展。
# 17. 如何实现数据驱动测试?
答案话术: 数据驱动主要用TestNG的@DataProvider实现。
我会把测试数据放在Excel或properties文件里,然后用@DataProvider读取:
@DataProvider(name = "loginData")
public Object[][] getLoginData() {
return new Object[][] {
{"user1", "pass1"},
{"user2", "pass2"},
{"user3", "pass3"}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password) {
loginPage.login(username, password);
}
这样一个测试方法就能跑多组数据,用于测试不同账号登录、不同商品下单等场景,避免写重复代码。
我们项目用Excel管理测试数据,用Apache POI读取,这样测试人员可以直接修改Excel,不用改代码。
# 18. 遇到过什么难点问题?如何解决的?
答案话术: 我遇到过一个比较棘手的问题:商品列表滑动加载更多时,元素定位不稳定,经常报NoSuchElementException。
分析后发现是因为列表采用懒加载,滑动时旧元素会被回收,新元素动态加载,导致定位时元素可能不在DOM树里。
我的解决方案是:
第一,滑动前先等待加载完成,通过检查加载动画消失来判断。
第二,用try-catch包裹滑动操作,如果定位失败就重试,最多重试3次。
第三,优化滑动策略,每次只滑动半屏而不是整屏,减少元素回收的概率。
优化后,用例稳定性从70%提升到95%以上。
# 19. 如何做持续集成?
答案话术: 我们通过Jenkins实现持续集成。
配置步骤是:
第一,在Jenkins创建自由风格任务,配置Git仓库地址,定时拉取最新代码。
第二,在构建环境配置Maven,执行mvn clean test命令运行测试。
第三,配置定时触发器,使用cron表达式设置每天凌晨2点自动执行。
第四,构建后操作配置Allure报告生成,并设置邮件通知,测试完成后自动发送报告给相关人员。
第五,如果测试失败,Jenkins会标记构建失败,并在邮件里附带失败截图和日志。
通过Jenkins,我们实现了每日自动回归测试,及时发现新版本引入的问题。
# 20. App自动化相比手工测试有什么优势和局限性?
答案话术: 优势主要有三个:
第一是效率高,手工回归核心流程需要2-3人日,自动化只需半小时,节省了大量时间。
第二是稳定可靠,自动化按固定步骤执行,不会因为人为疏忽遗漏测试点。
第三是支持持续集成,可以每天自动执行,及时发现问题。
但也有局限性:
第一是无法替代探索性测试,自动化只能验证预期结果,发现不了预期之外的问题。
第二是维护成本高,页面元素变化需要及时更新脚本,否则会大量失败。
第三是不适合频繁变化的功能,如果功能还不稳定,自动化会投入产出比很低。
所以我们策略是:稳定的核心流程做自动化,新功能和探索性测试还是手工为主。
🔗测试开发训练营 (opens new window):针对面试求职的测试开发训练营,大厂导师 1v1 私教,帮助学员从 0 到 1 拿到测开满意的 offer
- ✅直播授课+实时互动答疑,课程到就业贯穿企业级案例,由测试开发导师全程主讲,绝无其他助理老师代课。
- ✅大厂测试开发导师一对一求职陪跑,覆盖课程答疑+简历打磨+面试模拟面试+面试复盘等求职辅导
最新的图解文章都在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。
