昨天参加了公司的一个自动化测试的分享,有一些收获,记录一下。
1.主流的web端的UI自动化测试工具
基于浏览器API: selenium2.0,Watir(IE Driver)
基于JS 进行驱动:selenium1.0,sahi(ruby)
基于Windows GUI :QTP,AutoIt
2.实现原理(这里只列举第一种,第二种我没有实践,暂不研究)
基于浏览器API:
这里我在网上看了一下selenium2的实现原理,跟这个差不多。
参考:https://blog.csdn.net/ant_ren/article/details/7970793
selenium的工作原理:
1.用CS设计方式,将浏览器看作是remote server,有它自己的端口,将selenium的脚本执行端看作client。
2.client发送http request(操作放在json里面)发送给web server端,转化为浏览器的API,再封装成执行命令CommandExecutor ,发给浏览器去用native的操作执行
3.浏览器执行后,再返回给client
简化为:webdriver把浏览器的原生API包装了一下,通过conmmandExecutor来给浏览器发送http request请求(操作写在json里面),通过设置的端口,去请求浏览器,将发送的请求转化成浏览器的原生的命令,再将浏览器的返回,装进httpResponse里面,发回给客户端。
画了一张图
其他博客里面,引用的开发者的图
3.UI自动化的原则
- 不依赖环境,不改变环境
- 隔离变量和不变量
- 代码尽可能复用
4.使用的设计模式
三层:
- 界面表示层:一个页面封装到一个类中,一个方法对应一个控件,主要是查找控件,复杂控件操作,比如表格,需要再界面表示层实现
- 业务逻辑层:调用界面表示层的控件操作,完成业务逻辑,一般一个函数封装一个简单的业务操作。
- 测试用例层:调用业务层的函数完成测试,并验证结果,尽量不依赖环境,不破坏环境,不要直接调用界面层函数
5.源码解读,我用的是appium,不过appium和selenium是同源的,可以用来参考。
findElements代码
Response response = execute(DriverCommand.FIND_ELEMENTS, ImmutableMap.of("using", by, "value", using));
@SuppressWarnings("unchecked") protected List<WebElement> findElements(String by, String using) { if (using == null) { throw new IllegalArgumentException("Cannot find elements when the selector is null."); } Response response = execute(DriverCommand.FIND_ELEMENTS, ImmutableMap.of("using", by, "value", using)); Object value = response.getValue(); List<WebElement> allElements; try { allElements = (List<WebElement>) value; } catch (ClassCastException ex) { throw new WebDriverException("Returned value cannot be converted to List<WebElement>: " + value, ex); } for (WebElement element : allElements) { setFoundBy(this, element, by, using); } return allElements; }
findElement 返回的是执行语句的 response,执行语句如下,返回值是response
protected Response execute(String driverCommand, Map<String, ?> parameters) { Command command = new Command(sessionId, driverCommand, parameters); Response response; long start = System.currentTimeMillis(); String currentName = Thread.currentThread().getName(); Thread.currentThread().setName( String.format("Forwarding %s on session %s to remote", driverCommand, sessionId)); try { log(sessionId, command.getName(), command, When.BEFORE); response = executor.execute(command); log(sessionId, command.getName(), command, When.AFTER); if (response == null) { return null; } // Unwrap the response value by converting any JSON objects of the form // {"ELEMENT": id} to RemoteWebElements. Object value = converter.apply(response.getValue()); response.setValue(value); } catch (SessionNotFoundException e){ throw e; } catch (Exception e) { log(sessionId, command.getName(), command, When.EXCEPTION); String errorMessage = "Error communicating with the remote browser. " + "It may have died."; if (driverCommand.equals(DriverCommand.NEW_SESSION)) { errorMessage = "Could not start a new session. Possible causes are " + "invalid address of the remote server or browser start-up failure."; } UnreachableBrowserException ube = new UnreachableBrowserException(errorMessage, e); if (getSessionId() != null) { ube.addInfo(WebDriverException.SESSION_ID, getSessionId().toString()); } if (getCapabilities() != null) { ube.addInfo("Capabilities", getCapabilities().toString()); } throw ube; } finally { Thread.currentThread().setName(currentName); } try { errorHandler.throwIfResponseFailed(response, System.currentTimeMillis() - start); } catch (WebDriverException ex) { if (parameters != null && parameters.containsKey("using") && parameters.containsKey("value")) { ex.addInfo( "*** Element info", String.format( "{Using=%s, value=%s}", parameters.get("using"), parameters.get("value"))); } ex.addInfo(WebDriverException.DRIVER_INFO, this.getClass().getName()); if (getSessionId() != null) { ex.addInfo(WebDriverException.SESSION_ID, getSessionId().toString()); } if (getCapabilities() != null) { ex.addInfo("Capabilities", getCapabilities().toString()); } Throwables.propagate(ex); } return response; }
response的核心代码如下:执行command,然后用转化器将response的value转化成语句使用的代码
response = executor.execute(command); …… Object value = converter.apply(response.getValue()); response.setValue(value);
这里的converter.apply,是JsonToWebElementConverter的方法,就是用来把返回的json串转化为element的转化器
public Object apply(Object result) { if (result instanceof Collection<?>) { Collection<?> results = (Collection<?>) result; return Lists.newArrayList(Iterables.transform(results, this)); } if (result instanceof Map<?, ?>) { Map<?, ?> resultAsMap = (Map<?, ?>) result; if (resultAsMap.containsKey("ELEMENT")) { RemoteWebElement element = newRemoteWebElement(); element.setId(String.valueOf(resultAsMap.get("ELEMENT"))); element.setFileDetector(driver.getFileDetector()); return element; } else if (resultAsMap.containsKey("element-6066-11e4-a52e-4f735466cecf")) { RemoteWebElement element = newRemoteWebElement(); element.setId(String.valueOf(resultAsMap.get("element-6066-11e4-a52e-4f735466cecf"))); element.setFileDetector(driver.getFileDetector()); return element; } else { return Maps.transformValues(resultAsMap, this); } } if (result instanceof Number) { if (result instanceof Float || result instanceof Double) { return ((Number) result).doubleValue(); } return ((Number) result).longValue(); } return result; }
appium原理参考https://www.cnblogs.com/csj2018/p/9937511.html