极客时间测试专栏阅读总结——软件测试总体方案
关键点&&概念
- 测试数据准备方法:
- API调用生成测试数据
- 数据库操作生成测试数据
- 程序随机生成测试数据
- 以上方法组合生成测试数据
注意:
测试用例执行过程中,实时创建测试数据,我们通常称这种方式为 On-the-fly。
测试用例执行前,事先创建好“开箱即用”的测试数据,我们通常称这种方式为 Out-of-box。
- 接口测试和单元测试对比:
- 单元测试:关注的是单个服务或程序接口的输入或者输出是否正确
- 接口测试:关注的是多个程序服务或接口的组成的对外的业务接口的功能验证
- 测试分类:
- 白盒测试:在完全了解程序的逻辑,对程序逻辑进行的测试
- 灰盒测试:内部逻辑不完全可见,但可以通过工具知道有判断循环
- 黑盒测试:完全不清楚程序的实现方式
- 核心竞争力:
- 测试策略设计能力
测试执行力度,进度评估
工具、自动化框架选择
资源分配
风险评估 - 用例设计能力
基础方法使用
常见中间件,技术了解:比如缓存,代理服务器,系统架构 - 错误分析能力
代码能力
严禁的逻辑思维
- 测试策略设计能力
原理
-
selenium2.0 原理
当使用 Selenium2.0 启动浏览器 Web Browser 时,后台会同时启动基于 WebDriver Wire 协议的 Web Service 作为 Selenium 的 Remote Server,并将其与浏览器绑定。绑定完成后,Remote Server 就开始监听 Client 端的操作请求。
执行测试时,测试用例会作为 Client 端,将需要执行的页面操作请求以 Http Request 的方式发送给 Remote Server。该 HTTP Request 的 body,是以 WebDriver Wire 协议规定的 JSON 格式来描述需要浏览器执行的具体操作。
Remote Server 接收到请求后,会对请求进行解析,并将解析结果发给 WebDriver,由 WebDriver 实际执行浏览器的操作。
WebDriver 可以看做是直接操作浏览器的原生组件(Native Component),所以搭建测试环境时,通常都需要先下载浏览器对应的 WebDriver。 -
Appium原理:
-
本质上,Appium Server 是一个 Node.js 应用,接受来自 Appium Client 的请求,解析后通过 WebDriver 协议和设备端上的代理打交道。Appium Client 其实就是测试代码,使用对应语言的 Client 将基于 JSON Wire 协议的操作指令发给 Appium Server。
-
iOS:Appium Server 会把操作请求发送给 WebDriverAgent(简称 WDA),然后 WDA 再基于 XCUITest 完成 iOS 模拟器或者真机上的自动化操作;
-
Android:Appium Server 会把操作请求发送给 appium-UIautomator2-server,然后 appium-UIautomator2-server 再基于 UIAutomator V2 完成 Android 模拟器或者真机上的自动化操作。
-
总结:Appium 属于 C/S 架构,Appium Client 通过多语言支持的第三方库向 Appium Server 发起请求,基于 Node.js 的 Appium Server 会接受 Appium Client 发来的请求,接着和 iOS 或者 Android 平台上的代理工具打交道,代理工具在运行过程中不断接收请求,并根据 WebDriver 协议解析出要执行的操作,最后调用 iOS 或者 Android 平台上的原生测试框架完成测试。
-
框架&&工具&&解释
- 基于API测试:REST Assured
- java 单元测试框架:TestNG,Junit
- C语言测试工具:CppTest,Parasoft C/C++test
- java 代码覆盖率统计:JaCoCo,原理:基本方法注入(Instrumentation),注入就是在被测代码中自动插入用于覆盖率统计的探针(Probe)代码,并保证插入的探针代码不会给原代码带来任何影响。
- 对于 Java 代码来讲,根据注入目标的不同,可以分为源代码(Source Code)注入和字节码(Byte Code)注入两大类。基于 JVM 本身特性以及执行效率的原因,目前主流的工具基本都是使用字节码注入,注入的具体实现采用 ASM 技术。
- 两种方式
On-The-Fly模式:支持Java Agent的运行环境,代理方式,代表工具:JaCoCo
Offline 注入模式:无法实时获取代码覆盖率信息,只能在系统停机时下获取,不需要代理,代表工具:Cobertura
- JavaScript 代码覆盖率统计:Istanbul
- 测试代码分类:
- 驱动代码(Driver)指调用被测函数的代码。在单元测试过程中,驱动模块通常包括调用被测函数前的数据准备、调用被测函数以及验证相关结果。
- 桩代码(Stub)是用来代替真实代码的临时代码;比如,某个函数 A 的内部实现中调用了一个尚未实现的函数 B,为了对函数 A 的逻辑进行测试,那么就需要模拟一个函数 B,这个模拟的函数 B 的实现就是所谓的桩代码。
- 覆盖率:业务覆盖率,代码覆盖率
- 持续集成工具:jenkins,sonar
- 测试服务化 TaaS :Test as a Service,就是把所有的测试变成一个服务,比如准备数据的服务,mock接口的服务,小业务模块也是个服务,便于管理和调用,需要根据自己的实际情况规划,这个能力需要进一步加强!!!
- 数据驱动(数据与):采用数据和测试代码分离的形式,把数据集中化,保证当某些点或者逻辑发生改变的情况下,可在最小的代价下调试代码。数据驱动包括:输入数据,业务判断标识数据和断言数据
- 页面对象(Page Object)模型:这就是所谓的PO,人造概念,其实就是先把每个要操作的元素封装成一个变量,放到同一个类或者配置文件中,再把每个功能(比如;一个功能包括对多个元素的操作)封装成一个方法。每个页面就是一个类,开源工具Katalon Studio。
- 对象自动生成工具:自动生成页面对象模型的类,Katalon Studio。工具能在提供了URL后把当前页面所有控件定位生成在同一个Page Class里,但是中间有一些动态的信息的
- js测试工具:Jest
- E2E:端到端(end-to-end)UI测试是一种测试方法,它用来测试一个应用从头到尾的流程是否和设计时候所想的一样。简而言之,它从一个用户的角度出发,认为整个系统都是一个黑箱,只有UI会暴露给用户。
- Responsive Page:Web 页面是基于自适应网页(自适应网页设计【Responsive Web Design】)
- 微服务:就是把业务系统的单工程拆解成N个小服务,小模块,使得服务间不产生互相间的绝对影响。而分布式则是同一工程,部署在不同服务器,提高系统抗压能力。
- 前端测试工具:WebPagetest(https://www.webpagetest.org,https://github.com/WPO-Foundation/webpagetest)
- 生成器模式:builder pattern(java设计模式)
- 测试架构:执行测试的过程中用到的所有基础硬件设施以及相关的软件设施
- 测试基础架构:测试执行的机器或者集群的创建与维护、测试执行集群的容量规划、测试发起的控制、测试用例的组织以及测试用例的版本控制等等
- 元数据:管理数据的数据,记录业务数据从产生到变化甚至最终销毁的过程的数据
- python测试框架:Unittest、Doctest、pytest、Nose、tox、Unittest2、mockunittest.mock
- 全链路压测:是基于真实的生产环境来模拟海量的并发用户请求和数据,对整个业务链路进行压力测试,试图找到所有潜在性能瓶颈点并持续优化的实践。
- ROI:投资回报率
UI自动化
- 浏览器:普通浏览器,无头浏览器(Headless Browser,无界面浏览器,Google的Headless Chrome),
- 无界面浏览器测试框架:Google的Puppeteer(需要Node支持),PhantomJS(已经停止维护)
- GUI稳定性保障方法:
- 异常场景恢复模式————GUI自动化的异常处理后继续执行(浏览器内的弹窗或者浏览器外的异常恢复,只能处理已知错误)
- 模糊匹配————由于页面的细微变化,比如id是element_01变为element_02,需要自己二次开发,UFT就实现了,需要在测试报告中明确告知
- A/B测试————需要指明系统
- 页面加载缓慢,还有重试机制,重试元素,页面,业务等
- 前置的测试数据错误
- 测试范围:
- 只覆盖最核心且直接影响主营业务流程的 E2E 场景
- 所有业务点覆盖率达到70%到80%
- 测试报告:
- 扩展selenium,完成截图,且对关键的元素高亮显示
- 在Hook(HOOK:钩子,挂钩,是一种实现Windows平台下类似于中断的机制)中操作,完成截图功能
- ???进一步了解测试报告
- 测试方法:
- 代码重用
- 代码重用
app测试
- app应用测试特点
- Web App 指的是移动端的 Web 浏览器。
- Native App 指的是移动端的原生应用, 对于 Android 是 apk,对于 iOS 就是 ipa。
- Hybrid App(俗称:混血应用),是介于 Web App 和 Native App 两者之间的一种 App 形式。通过一个原生实现的 Native Container 展示 HTML5 的页面。在原生移动应用中嵌入了 Webview,然后通过该 Webview 来访问网页。
- iOS 一般采用 XCUITest Driver,而 Android 一般采用 UiAutomator2 或者 Espresso等
- Hybrid App测试注意点:Native Container 和 Webview 分别属于两个不同的上下文(Context),Native Container 默认的 Context 为“NATIVE APP",而 Webview 默认的 Context 为“WEBVIEW_+ 被测进程名称”。
- 六项专项测试:
交叉事件测试:切换应用:例如电话、短信、提示升级、闹钟、前后台切换、切换网络
兼容性测试:屏幕大小,系统版本,不同机型,不同网络
流量测试:系统自带监控或者监控软件,例如:tcpdump、Wireshark 和 Fiddler
耗电量测试:Android 命令“adb shell dumpsys battery”来获取应用的耗电量信息;iOS 通过 Apple 的官方工具 Sysdiagnose 来收集耗电量信息,后,可以进一步通过 Instrument 工具链中的 Energy Diagnostics 进行耗电量分析。
弱网络测试:移动网络测试工具Augmented Traffic Control(ATC)
边界测试:内存占用超过90%,存储占用超过95%,系统时间偏差,长时间使用 - 被测Demo:
Native App:https://github.com/appium/ios-test-app
Web App:http://appium.io - IOS开发工具Xcode,模拟器Simulator
- APP多机同步测试框架:Appium + OpenSTF + Selenium Gird
API测试——接口测试
- API测试工具:命令行工具 cURL、Postman、SoapUI、JMeter(Newman插件???)
- API被测Demo:https://github.com/SpectoLabs/spring-cloud-contract-blog
- Java API测试框架:OkHttP、Unirest
- Python API测试框架:http.client、Requests、HttpRunner
- NodeJS API测试框架:Native、Request
- 基于契约的测试方法:关注服务的使用者,比如该服务有固定的调用者,而且不会乱传参数。
代码级测试——单元测试
- 代码测试方法
- 人工静态方法:通过人工阅读代码查找代码中潜在错误的方法,通常采用的手段包括,开发人员代码走查、结对编程、同行评审等
- 自动静态方法:发现语法特征错误、边界行为特征错误和经验特征错误这三类“有特征”的错误,工具:SonarLint,SonarQube
- 人工动态方法:设计代码的输入和预期的正确输出的集合,然后执行代码,判断实际输出是否符合预期。我在之前的第三篇文章
- 自动动态方法,又称自动边界测试方法,指的是基于代码自动生成边界测试用例并执行,以捕捉潜在的异常、崩溃和超时的方法
- 动态测试方法入参:
- 定义的变量值
- 静态变量(函数中已经定义的特定值)
- 成员变量
- 调用的函数返回值和被调用函数的返回值修改的变量
- 嵌入式系统中,在中断调用中改写的数据
- 动态测试方法预期:
- 程序运行后的返回值
- 程序运行后的输出
- 程序运行后改变的全局变量或成员变量
- 程序运行后改变的文件,数据库中的数据,消息队列
性能测试
-
性能测试需要关注的是算法设计、架构设计、性能最佳实践、数据库相关、软件性能的可测试性这五大方面。
-
性能测试需要关注的具体内容:
- 算法设计包含的点:
核心算法的设计与实现是否高效
必要时,设计上是否采用 buffer 机制以提高性能,降低 I/O
是否存在潜在的内存泄露
是否存在并发环境下的线程安全问题
是否存在不合理的线程同步方式
是否存在不合理的资源竞争 - 架构设计包含的内容:
站在整体系统的角度,是否可以方便地进行系统容量和性能扩展
应用集群的可扩展性是否经过测试和验证
缓存集群的可扩展性是否经过测试和验证
数据库的可扩展性是否经过测试和验证 - 性能最佳实践包含的点:
代码实现是否遵守开发语言的性能最佳实践
关键代码是否在白盒级别进行性能测试
是否考虑前端性能的优化
必要的时候是否采用数据压缩传输
对于既要压缩又要加密的场景,是否采用先压缩后加密的顺序 - 数据库相关的点:
数据库表设计是否高效
是否引入必要的索引
SQL 语句的执行计划是否合理
SQL 语句除了功能是否要考虑性能要求
数据库是否需要引入读写分离机制
系统冷启动后,缓存大量不命中的时候,数据库承载的压力是否超负荷 - 软件性能的可测试性包含的点:
是否支持高并发场景下的性能打点
是否支持全链路的性能分析
- 算法设计包含的点:
-
性能测试的能力
- 性能需求的总结和抽象能力
- 根据性能测试目标,精准的性能测试场景设计和计算能力
- 性能测试场景和性能测试脚本的开发和执行能力
- 测试性能报告的分析解读能力
- 性能瓶颈的快速排查和定位能力
- 性能测试数据的设计和实现能力
- 面对互联网产品,全链路压测的设计与执行能力,能够和系统架构师一起处理流量标记、影子数据库等的技术设计能力
- 深入理解性能测试工具的内部实现原理,当性能测试工具有限制时,可以进行扩展二次开发
- 极其宽广的知识面,既要有“面”的知识,比如系统架构、存储架构、网络架构等全局的知识,还要有大量“点”的知识积累,比如数据库 SQL 语句的执行计划调优、JVM 垃圾回收(GC)机制、多线程常见问题等等
-
并发用户数:业务上认为是最大最大在线人数,但对于服务的性能来讲,是在线的人想发起请求数,同时要注意用户发起的哪类请求较多
-
响应时间标准定义:应用系统从请求发出开始,到客户端接收到最后一个字节数据所消耗的时间
- 响应时间:分为前端展现时间和系统响应时间两部分。其中,前端时间,又称呈现时间,取决于客户端收到服务器返回的数据后渲染页面所消耗的时间;而系统响应时间,又可以进一步划分为 Web 服务器时间、应用服务器时间、数据库时间,以及各服务器间通信的网络时间。
- 如果响应时间无法提升,可以通过在没有完全接受就加载的技术实现部分加载,提升用户体验
-
系统吞吐量:单位时间内的请求数量,请求的字节大小,页面多少
- “Bytes/Second”和“Pages/Second”表示的吞吐量,主要受网络设置、服务器架构、应用服务器制约;
- “Requests/Second”表示的吞吐量,主要受应用服务器和应用本身实现的制约。
-
性能测试方法分类:
- 后端性能测试(Back-end Performance Test):通过性能测试工具模拟大量的并发用户请求,然后获取系统性能的各项指标,并且验证各项指标是否符合预期的性能需求的测试手段
- 前端性能测试(Front-end Performance Test):
通常来讲,前端性能关注的是浏览器端的页面渲染时间、资源加载顺序、请求数量、前端缓存使用情况、资源压缩等内容,希望借此找到页面加载过程中比较耗时的操作和资源,然后进行有针对性的优化,最终达到优化终端用户在浏览器端使用体验的目的。
优化方法:减少 http 请求次数,减少 DNS 查询次数,避免页面跳转,使用内容分发网络(CDN),Gzip 压缩传输文件
雅虎军规:https://developer.yahoo.com/performance/rules.html?guccounter=1 - 代码级性能测试(Code-level Performance Test):简单的重复单元测试
- 压力测试(Load/Stress Test)
- 配置测试(Configuration Test):操作系统配置、应用服务器配置、jvm配置、数据库配置、网络配置等
- 并发测试(Concurrence Test):同时调用后端服务,期间观察被调用服务在并发情况下的行为表现,旨在发现诸如资源竞争、资源死锁之类的问题
- 可靠性测试(Reliability Test):通过长时间模拟真实的系统负载来发现系统潜在的内存泄漏、链接池回收等问题,一般需要3-7天
-
性能测试领域:能力验证、能力规划、性能调优、缺陷发现
-
性能测试步骤
- 性能需求获取
- 性能场景设计
- 性能测试脚本开发
- 性能场景实现
- 性能测试执行
- 性能结果报告分析
- 性能优化和再验证
-
性能工具组成:虚拟用户脚本生成器、压力控制器、压力产生器、系统监控器、测试结果分析器
-
负载策略
-
全链路疑难点解决
- 海量并发请求的发起主要借助于 JMeter,并且通过 Jenkins Job 来实现海量并发的调度控制(小型可以用分布式的 JMeter);
- 全链路压测流量和数据的隔离主要借助含有特定标记的流量和数据来实现,同时需要对业务模块以及中间件进行必要的改造,数据库这边还会使用影子数据库(数据分发就是要自己开发,哇咔咔);
- 实际业务负载的模拟,主要是采用基于历史流量修改后的回放来实现;
- 全链路压测完成后的数据清洗,则是借助自动化的手段来批量完成。
测试数据
- 生成方式:接口,数据库生成,随机程序生成。
- 方案:使用java的生成器模式和restful API开发一个数据准备平台
集成测试环境
-
Selenium Hub 用来管理各个 Selenium Node 的注册信息和状态信息,并且接收远程客户端代码的测试调用请求,并把请求命令转发给符合要求的 Selenium Node 执行。
-
搭建一般的selenium grid
-
在Docker上搭建 selenium grid
-
在云端搭建selenium grid
-
注意以下流程,这样可以实现大型系统的统一控制,如果是小型系统可以不使用统一测试平台,和jenkins集群,甚至不需要把selenium grid部署在docker上,服务器自动扩容等
-
注意实现selenium grid的远程控制并发,持续集成,使用人员
-
测试服务化:
- 统一测试执行服务:以 Restful API 的形式对外提供测试执行服务,兼具了测试版本管理、Jenkins 测试 Job 管理,以及测试执行结果管理的能力
- 统一测试数据服务:以 Restful API 的形式对外提供测试数据服务,元数据管理,测试数据自动补全,测试数据质量监控(好高级)
- 全局测试配置服务:比如要测试的不同国家,不同地域,不同语言等配置
- 测试报告服务:根据不同测试,比如GUI测试或者是API测试给出不同的测试报告,并存储测试报告
- 测试执行环境准备服务:根据测试数据服务,测试内容控制selenium接点和是否扩容(我有生之年还可以用到吗???)
- 被测系统部署服务:以 Restful API 的形式对外提供部署环境,或者安装APP等服务
测试工作思维
- 测试驱动开发:Test-Driven Development,通常简称为 TDD,测试确认好需求,用例给开发,让开发更有目的。
- 探索式测试:了解了需求后,预测到业务上或者实现上可能出现问题,进行有目的的测试。
- 精准测试:借助一定的技术手段、通过算法的辅助对传统软件测试过程进行可视化、分析以及优化的过程。
- 理解;技术手段分析用例、数据、代码之间的联系并把记录可视化
- 精准测试范例:http://www.threadingtest.com/index.html
- 核心技术:
- 软件测试精准显示波:在人工或者自动化测试过程中记录逻辑块,代码条件函数调用等的速率,并记录成图表
- 测试用例和被测产品代码的双向追溯:双向关联代码和用例
- 智能回归测试用例选取算法:智能选取改动代码影响的测试用例
- 测试用例的聚类分析:把用例和业务关联(就是做一个分类,和2,3有区别??)
安全测试
- 渗透测试:由专业安全人员模拟黑客,从其可能存在的位置对系统进行攻击测试,在真正的黑客入侵前找到隐藏的安全漏洞,从而达到保护系统安全的目的。
- 渗透测试分类:
- 有针对性的测试:“开灯”测试,在了解内部所有信息(代码,架构,环境)基础下的测试外部测试
- 针对外部可见的服务器和设备(包括:域名服务器(DNS)、Web 服务器或防火墙、电子邮箱服务器等等),模拟外部攻击者对其进行攻击,检查它们是否能够被入侵,以及如果被成功入侵了,会被入侵到系统的哪一部分、又会泄露多少资料
- 内部测试;由测试工程师模拟内部人员,在内网(防火墙以内)进行攻击,因此测试人员会拥有较高的系统权限,也能够查看各种内部资料,目的是检查内部攻击可以给系统造成什么程度的损害
- 盲测;严格限制提供给测试执行人员或团队信息的前提下,由他们来模拟真实攻击者的行为和上下文。通常,测试人员可能只被告知被测系统公开的信息,而对系统细节以及内部实现一无所知
- 双盲测试:也叫作“隐秘测试”,不光测试人员对系统内部知之甚少,而且被测系统内部也只有极少数人知道正在进行安全测试。因此,双盲测试可以反映软件系统最真实的安全状态,能够有效地检测系统在正常情况下,对安全事件的监控和处理能力是否合格
- 渗透测试步骤
- 规划和侦察:定义测试的范围和目标、初步确定要使用的工具和方法、明确需要收集的情报(例如,网络和域名,邮件服务器)
- 安全扫描:包括静态分析(扫描所有代码来估计其运行时的方式,工具Fortify SCA 和 Checkmarx Suite)和动态分析两个阶段(代码运行时进行扫描)。
- 获取访问权限:模拟黑客对应用程序进行网络攻击,例如使用 SQL 注入或者XSS 跨站脚本攻击,发现系统漏洞,利用漏洞升级自己的权限、窃取数据、拦截流量等方式了解其可能对系统造成的损害
- 维持访问权限,查看被发现的漏洞是否可以长期存在于系统,或者通过手段保持漏洞存在
- 入侵分析:可以被利用的特定漏洞;利用该漏洞的具体步骤;能够被访问的敏感数据;渗透测试人员能够在系统中不被侦测到的存在时间。
- 渗透测试工具
- Nmap 是进行主机检测和网络扫描的重要工具。用来收集系统基本信息(IP,端口),漏洞探测和安全扫描,从主机发现、端口扫描到操作系统检测和 IDS 规避 / 欺骗。渗透测试中最先用到的,适用于Windows、Linux、OSX等系统
- Aircrack-ng 是评估 Wi-Fi 网络安全性的一整套工具。主要功能有:网络侦测、数据包嗅探、WEP 和 WPA/WPA2-PSK 破解。Aircrack-ng 可以工作在任何支持监听模式的无线网卡上并嗅探 802.11a、802.11b、802.11g 的数据。
Aircrack-ng 的执行是通过命令行或者脚本文件的方式,可以运行在 Linux 和 Windows 操作系统上。它的典型应用场景,主要包括数据包注入重播攻击、解除身份验证、虚假接入点等,也可以用于破解 WEP 和 WPA PSK。 - sqlmap 是一种开源的基于命令行的渗透测试工具。它能够自动进行 SQL 注入和数据库接入,并且支持所有常见并广泛使用的数据库平台,包括 Oracle、MySQL、Microsoft SQL Server、SQLite、Microsoft Access、IBM DB2、FireBird、Sybase 和 SAP Max DB 等,使用的 SQL 注入技术也几乎涵盖了所有的攻击手段。
- Wifiphisher 是一种恶意接入点工具,可以对 WiFi 网络进行自动钓鱼攻击。渗透测试执行人员,可以通过 Wifiphisher 执行有针对性的 WiFi 关联攻击,实现无线客户端的渗透测试。Wifiphisher 还可以用于对连接的客户端进行受害者定制的网络钓鱼攻击,用来获取凭证(例如,从第三方登录页面或 WPA/WPA2 预共享密钥)或用恶意软件感染受害者站点。
- AppScan 是 IBM 公司的一款企业级商业 Web 应用安全测试工具,采用的是黑盒测试,可以扫描常见的 Web 应用安全漏洞。
工作原理首先,从起始页爬取站下所有的可见页面,同时测试常见的管理后台;然后,利用 SQL 注入原理测试所有可见页面,是否在注入点和跨站脚本攻击的可能;同时,检测 Cookie 管理、会话周期等常见的 Web 安全漏洞。
基于模型的测试
- 定义:Model-Based-Testing,简称 MBT,是自动化测试的一个分支。它是将测试用例的设计依托于被测系统的模型,并基于该模型自动生成测试用例的技术。其中,这个被测系统的模型表示了被测系统行为的预期,也可以说是代表了我们对被测系统的预期。
- MBT 的基本原理是通过建立被测系统的设计模型,然后结合不同的算法和策略来遍历该模型,以此生成测试用例的设计。‘
- 常用模型,有限状态机:不同组合对应不同状态,状态图,UML
- MBT 工具简介 BPM-X fMBT
- 优缺点:优点已维护,缺点难学,花钱
试试
- spring boot + Cucumber + REST Assured + selenium
posted on 2019-06-10 10:46 onesilent 阅读(1184) 评论(1) 编辑 收藏 举报