<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾测试推荐</title>
    <link>https://itindex.net/categories/测试</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/categories/测试</link>
    </image>
    <item>
      <title>如何为复杂的 Java 应用编写集成测试</title>
      <link>https://itindex.net/detail/62942-%E5%A4%8D%E6%9D%82-java-%E5%BA%94%E7%94%A8</link>
      <description>&lt;p&gt;最近有时间又把以前开源的   &lt;a href="https://github.com/crossoverJie/cim"&gt;IM 消息系统&lt;/a&gt;捡起来继续开发了（确实这些年经常有朋友催更）。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;没错，确实是这些年，因为上次发版还是再 2019 年的八月份。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;这段时间比较重大的更新就是把  &lt;a href="https://github.com/crossoverJie/cim/pull/140"&gt;元数据中心&lt;/a&gt;抽离出来了，以前是和 zookeeper 的代码强耦合在一起的，重构之后可以有多种实现了。&lt;/p&gt; &lt;p&gt;今后甚至可以提供一个 jar 包就可以把后端服务全部启动起来用于体验，此时就可以使用一个简单的基于内存的注册中心。&lt;/p&gt; &lt;p&gt;除此之外做的更多的就是新增了一个集成测试的模块，没有完善的集成测试功能在合并代码的时候都要小心翼翼，基本的功能需求都没法保证。&lt;/p&gt; &lt;p&gt;加上这几年我也接触了不少优秀的开源项目（比如 Pulsar、OpenTelemetry、HertzBeat 等），他们都有完整的代码合并流程；首先第一点就得把测试流水线跑通过。&lt;/p&gt; &lt;p&gt;这一点在 OpenTelemetry 社区更为严格：&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/04/wlaPtbJNux5vc9n.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;他们的构建测试流程非常多，包括单元测试、集成测试、代码风格、多版本兼容等。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;所以在结合了这些优秀项目的经验后我也为 cim 项目新增相关的模块   &lt;a href="https://github.com/crossoverJie/cim/pull/144"&gt;cim-integration-test&lt;/a&gt;，同时也在 github 上配置了相关的 action，最终的效果如下：&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/04/p9tLcvPTlMBIVq4.png"&gt;&lt;/img&gt;  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/09/04/Rxw5F8kNWT1pDO7.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;在   &lt;code&gt;“Build with Maven”&lt;/code&gt; 阶段触发单元测试和集成测试，最终会把测试结果上传到 Codecov，然后会在 PR 的评论区输出测试报告。  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/09/04/zmPHB16ryCtGoEM.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;相关的 action 配置如下：&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/05/mteK1Yj43Z7gIrR.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;就是配置了几个 Job，重点是这里的：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;mvn -B package --file pom.xml     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;它会编译并运行项目下面的所有 test 代码。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#cim-integration-test-&amp;#27169;&amp;#22359;" title="cim-integration-test &amp;#27169;&amp;#22359;"&gt;&lt;/a&gt;cim-integration-test 模块&lt;/h1&gt; &lt;p&gt;为了方便进行集成测试，我新增了   &lt;code&gt;cim-integration-test&lt;/code&gt; 这个模块，这里面没有任何源码，只有测试相关的代码。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/05/RfK8FVrL916D7cI.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;类的继承关系图如下：&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/05/75U9vbkPZOgqrRx.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;因为我们做集成测试需要把 cim 所依赖的服务都启动起来，目前主要由以下几个服务：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;cim-server: cim 的服务端&lt;/li&gt;  &lt;li&gt;cim-route: 路由服务&lt;/li&gt;  &lt;li&gt;cim-client: 客户端&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;而 route 服务是依赖于 server 服务，所以 route 继承了 server，client 则是需要 route 和 server 都启动，所以它需要继承 route。&lt;/p&gt; &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#38598;&amp;#25104;-test-container" title="&amp;#38598;&amp;#25104; test container"&gt;&lt;/a&gt;集成 test container&lt;/h2&gt; &lt;p&gt;先来看看 server 的测试实现：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;public abstract class AbstractServerBaseTest {       &lt;br /&gt;       &lt;br /&gt;    private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName       &lt;br /&gt;            .parse(&amp;quot;zookeeper&amp;quot;)       &lt;br /&gt;            .withTag(&amp;quot;3.9.2&amp;quot;);       &lt;br /&gt;       &lt;br /&gt;    private static final Duration DEFAULT_STARTUP_TIMEOUT = Duration.ofSeconds(60);       &lt;br /&gt;       &lt;br /&gt;    @Container       &lt;br /&gt;    public final ZooKeeperContainer       &lt;br /&gt;            zooKeeperContainer = new ZooKeeperContainer(DEFAULT_IMAGE_NAME, DEFAULT_STARTUP_TIMEOUT);       &lt;br /&gt;       &lt;br /&gt;    @Getter       &lt;br /&gt;    private String zookeeperAddr;       &lt;br /&gt;       &lt;br /&gt;    public void startServer() {       &lt;br /&gt;        zooKeeperContainer.start();       &lt;br /&gt;        zookeeperAddr = String.format(&amp;quot;%s:%d&amp;quot;, zooKeeperContainer.getHost(), zooKeeperContainer.getMappedPort(ZooKeeperContainer.DEFAULT_CLIENT_PORT));       &lt;br /&gt;        SpringApplication server = new SpringApplication(CIMServerApplication.class);       &lt;br /&gt;        server.run(&amp;quot;--app.zk.addr=&amp;quot; + zookeeperAddr);       &lt;br /&gt;    }       &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;因为   &lt;code&gt;server&lt;/code&gt; 是需要依赖   &lt;code&gt;zookeeper&lt;/code&gt; 作为元数据中心，所以在启动之前需要先把 zookeeper 启动起来。&lt;/p&gt; &lt;p&gt;此时就需要使用   &lt;a href="https://testcontainers.com/"&gt;testcontainer&lt;/a&gt; 来做支持了，使用它可以在单测的过程中使用 docker 启动任意一个服务，这样在 CI 中做集成测试就很简单了。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/06/X3vzp5qd7tbAIHB.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;我们日常使用的大部分中间件都是支持的，使用起来也很简单。&lt;/p&gt; &lt;p&gt;先添加相关的依赖：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;dependencies&amp;gt;     &lt;br /&gt;    &amp;lt;dependency&amp;gt;     &lt;br /&gt;        &amp;lt;groupId&amp;gt;org.postgresql&amp;lt;/groupId&amp;gt;     &lt;br /&gt;        &amp;lt;artifactId&amp;gt;postgresql&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;        &amp;lt;version&amp;gt;42.7.3&amp;lt;/version&amp;gt;     &lt;br /&gt;    &amp;lt;/dependency&amp;gt;     &lt;br /&gt;    &amp;lt;dependency&amp;gt;     &lt;br /&gt;        &amp;lt;groupId&amp;gt;ch.qos.logback&amp;lt;/groupId&amp;gt;     &lt;br /&gt;        &amp;lt;artifactId&amp;gt;logback-classic&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;        &amp;lt;version&amp;gt;1.5.6&amp;lt;/version&amp;gt;     &lt;br /&gt;    &amp;lt;/dependency&amp;gt;     &lt;br /&gt;    &amp;lt;dependency&amp;gt;     &lt;br /&gt;        &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;     &lt;br /&gt;        &amp;lt;artifactId&amp;gt;junit-jupiter&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;        &amp;lt;version&amp;gt;5.10.2&amp;lt;/version&amp;gt;     &lt;br /&gt;        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;     &lt;br /&gt;    &amp;lt;/dependency&amp;gt;     &lt;br /&gt;&amp;lt;/dependencies&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;然后在选择我们需要依赖的服务，比如是   &lt;code&gt;PostgreSQL&lt;/code&gt;：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;dependency&amp;gt;     &lt;br /&gt;    &amp;lt;groupId&amp;gt;org.testcontainers&amp;lt;/groupId&amp;gt;     &lt;br /&gt;    &amp;lt;artifactId&amp;gt;postgresql&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;    &amp;lt;version&amp;gt;1.19.8&amp;lt;/version&amp;gt;     &lt;br /&gt;    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;     &lt;br /&gt;&amp;lt;/dependency&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;然后在测试代码中启动相关的服务&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;23     &lt;br /&gt;24     &lt;br /&gt;25     &lt;br /&gt;26     &lt;br /&gt;27     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;class CustomerServiceTest {     &lt;br /&gt;     &lt;br /&gt;  static PostgreSQLContainer&amp;lt;?&amp;gt; postgres = new PostgreSQLContainer&amp;lt;&amp;gt;(     &lt;br /&gt;    &amp;quot;postgres:16-alpine&amp;quot;     &lt;br /&gt;  );     &lt;br /&gt;     &lt;br /&gt;  CustomerService customerService;     &lt;br /&gt;     &lt;br /&gt;  @BeforeAll     &lt;br /&gt;  static void beforeAll() {     &lt;br /&gt;    postgres.start();     &lt;br /&gt;  }     &lt;br /&gt;     &lt;br /&gt;  @AfterAll     &lt;br /&gt;  static void afterAll() {     &lt;br /&gt;    postgres.stop();     &lt;br /&gt;  }     &lt;br /&gt;     &lt;br /&gt;  @BeforeEach     &lt;br /&gt;  void setUp() {     &lt;br /&gt;    DBConnectionProvider connectionProvider = new DBConnectionProvider(     &lt;br /&gt;      postgres.getJdbcUrl(),     &lt;br /&gt;      postgres.getUsername(),     &lt;br /&gt;      postgres.getPassword()     &lt;br /&gt;    );     &lt;br /&gt;    customerService = new CustomerService(connectionProvider);     &lt;br /&gt;  }     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;通常情况下我们都是需要获取这些中间件的链接，比如 IP 端口啥的。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;org.testcontainers.containers.ContainerState#getHost     &lt;br /&gt;org.testcontainers.containers.ContainerState#getMappedPort     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;通常是通过这两个函数来获取对应的 IP 和端口。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#38598;&amp;#25104;" title="&amp;#38598;&amp;#25104;"&gt;&lt;/a&gt;集成&lt;/h1&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Container       &lt;br /&gt;RedisContainer redis = new RedisContainer(DockerImageName.parse(&amp;quot;redis:7.4.0&amp;quot;));       &lt;br /&gt;       &lt;br /&gt;public void startRoute() {       &lt;br /&gt;    redis.start();       &lt;br /&gt;    SpringApplication route = new SpringApplication(RouteApplication.class);       &lt;br /&gt;    String[] args = new String[]{       &lt;br /&gt;            &amp;quot;--spring.data.redis.host=&amp;quot; + redis.getHost(),       &lt;br /&gt;            &amp;quot;--spring.data.redis.port=&amp;quot; + redis.getMappedPort(6379),       &lt;br /&gt;            &amp;quot;--app.zk.addr=&amp;quot; + super.getZookeeperAddr(),       &lt;br /&gt;    };         &lt;br /&gt;    route.setAdditionalProfiles(&amp;quot;route&amp;quot;);       &lt;br /&gt;    route.run(args);       &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;对于 route 来说不但需要   &lt;code&gt;zookeeper&lt;/code&gt; 还需要   &lt;code&gt;Redis&lt;/code&gt; 来存放用户的路由关系，此时就还需要运行一个 Redis 的容器，使用方法同理。&lt;/p&gt; &lt;p&gt;最后就需要以   &lt;code&gt;springboot&lt;/code&gt; 的方式将这两个应用启动起来，我们直接创建一个   &lt;code&gt;SpringApplication&lt;/code&gt; 对象，然后将需要修改的参数通过   &lt;code&gt;--varname=value&lt;/code&gt; 的形式将数据传递进去。&lt;/p&gt; &lt;p&gt;还可以通过   &lt;code&gt;setAdditionalProfiles()&lt;/code&gt; 函数指定当前应用运行的 profile，这样我们就可以在测试目录使用对应的配置文件了。&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://s2.loli.net/2024/09/06/ySK2akYOIAPJqUH.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;route.setAdditionalProfiles(&amp;quot;route&amp;quot;);       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;比如我们这里设置为 route 就可以使用   &lt;code&gt;application-route.yaml&lt;/code&gt; 作为 route 的配置文件启动，就不用每个参数都通过   &lt;code&gt;--&lt;/code&gt; 传递了。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;private void login(String userName, int port) throws Exception {       &lt;br /&gt;    Long userId = super.registerAccount(userName);       &lt;br /&gt;    SpringApplication client = new SpringApplication(CIMClientApplication.class);       &lt;br /&gt;    client.setAdditionalProfiles(&amp;quot;client&amp;quot;);       &lt;br /&gt;    String[] args = new String[]{       &lt;br /&gt;            &amp;quot;--server.port=&amp;quot; + port,       &lt;br /&gt;            &amp;quot;--cim.user.id=&amp;quot; + userId,       &lt;br /&gt;            &amp;quot;--cim.user.userName=&amp;quot; + userName       &lt;br /&gt;    };       &lt;br /&gt;    client.run(args);       &lt;br /&gt;}       &lt;br /&gt;       &lt;br /&gt;@Test       &lt;br /&gt;public void olu() throws Exception {       &lt;br /&gt;    super.startServer();       &lt;br /&gt;    super.startRoute();       &lt;br /&gt;    this.login(&amp;quot;crossoverJie&amp;quot;, 8082);       &lt;br /&gt;    this.login(&amp;quot;cj&amp;quot;, 8182);       &lt;br /&gt;    MsgHandle msgHandle = SpringBeanFactory.getBean(MsgHandle.class);       &lt;br /&gt;    msgHandle.innerCommand(&amp;quot;:olu&amp;quot;);       &lt;br /&gt;    msgHandle.sendMsg(&amp;quot;hello&amp;quot;);       &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;我们真正要测试的其实是客户端的功能，只要客户端功能正常，说明 server 和 route 也是正常的。&lt;/p&gt; &lt;p&gt;比如这里的   &lt;code&gt;olu(oline user)&lt;/code&gt; 的测试流程是：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;启动 server 和 route&lt;/li&gt;  &lt;li&gt;登录注册两个账号&lt;/li&gt;  &lt;li&gt;查询出所有用户&lt;/li&gt;  &lt;li&gt;发送消息&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;最终的测试结果如下，符合预期。&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://s2.loli.net/2024/09/06/uX7BrNwC8iOHqSQ.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#30896;&amp;#21040;&amp;#30340;&amp;#38382;&amp;#39064;" title="&amp;#30896;&amp;#21040;&amp;#30340;&amp;#38382;&amp;#39064;"&gt;&lt;/a&gt;碰到的问题&lt;/h1&gt; &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#24212;&amp;#29992;&amp;#20998;&amp;#23618;" title="&amp;#24212;&amp;#29992;&amp;#20998;&amp;#23618;"&gt;&lt;/a&gt;应用分层&lt;/h2&gt; &lt;p&gt;不知道大家注意到刚才测试代码存在的问题没有，主要就是没法断言。&lt;/p&gt; &lt;p&gt;因为客户端、route、server 都是以一个应用的维度去运行的，没法获取到一些关键指标。&lt;/p&gt; &lt;p&gt;比如输出在线用户，当客户端作为一个应用时，在线用户就是直接打印在了终端，而没有直接暴露一个接口返回在线数据；收发消息也是同理。&lt;/p&gt; &lt;p&gt;其实在应用内部这些都是有接口的，但是作为一个整体的   &lt;code&gt;springboot&lt;/code&gt; 应用就没有提供这些能力了。&lt;/p&gt; &lt;p&gt;本质上的问题就是这里应该有一个 client-sdk 的模块，client 也是基于这个 sdk 实现的，这样就可以更好的测试相关的功能了。&lt;/p&gt; &lt;p&gt;之后就准备把 sdk 单独抽离一个模块，这样可以方便基于这个 sdk 实现不同的交互，甚至做一个 UI 界面都是可以的。&lt;/p&gt; &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#32534;&amp;#35793;&amp;#22833;&amp;#36133;" title="&amp;#32534;&amp;#35793;&amp;#22833;&amp;#36133;"&gt;&lt;/a&gt;编译失败&lt;/h2&gt; &lt;p&gt;还有一个问题就是我是直接将   &lt;code&gt;client/route/server&lt;/code&gt; 的依赖集成到   &lt;code&gt;integration-test&lt;/code&gt; 模块中：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;dependency&amp;gt;       &lt;br /&gt;  &amp;lt;groupId&amp;gt;com.crossoverjie.netty&amp;lt;/groupId&amp;gt;       &lt;br /&gt;  &amp;lt;artifactId&amp;gt;cim-server&amp;lt;/artifactId&amp;gt;       &lt;br /&gt;  &amp;lt;version&amp;gt;${project.version}&amp;lt;/version&amp;gt;       &lt;br /&gt;  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;       &lt;br /&gt;&amp;lt;/dependency&amp;gt;       &lt;br /&gt;       &lt;br /&gt;&amp;lt;dependency&amp;gt;       &lt;br /&gt;  &amp;lt;groupId&amp;gt;com.crossoverjie.netty&amp;lt;/groupId&amp;gt;       &lt;br /&gt;  &amp;lt;artifactId&amp;gt;cim-forward-route&amp;lt;/artifactId&amp;gt;       &lt;br /&gt;  &amp;lt;version&amp;gt;${project.version}&amp;lt;/version&amp;gt;       &lt;br /&gt;  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;       &lt;br /&gt;&amp;lt;/dependency&amp;gt;       &lt;br /&gt;       &lt;br /&gt;&amp;lt;dependency&amp;gt;       &lt;br /&gt;  &amp;lt;groupId&amp;gt;com.crossoverjie.netty&amp;lt;/groupId&amp;gt;       &lt;br /&gt;  &amp;lt;artifactId&amp;gt;cim-client&amp;lt;/artifactId&amp;gt;       &lt;br /&gt;  &amp;lt;version&amp;gt;${project.version}&amp;lt;/version&amp;gt;       &lt;br /&gt;  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;       &lt;br /&gt;&amp;lt;/dependency&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;在 IDEA 里直接点击测试按钮是可以直接运行这里的测试用例的，但是想通过   &lt;code&gt;mvn test&lt;/code&gt; 时就遇到了问题。&lt;/p&gt; &lt;p&gt;  &lt;img alt="image.png" src="https://s2.loli.net/2024/09/06/DFy6otpPvjar4JM.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;会在编译期间就是失败了，我排查了很久最终发现是因为这三个模块应用使用了springboot 的构建插件：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;plugin&amp;gt;     &lt;br /&gt;&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;     &lt;br /&gt;&amp;lt;artifactId&amp;gt;spring-boot-maven-plugin&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;&amp;lt;executions&amp;gt;     &lt;br /&gt;&amp;lt;execution&amp;gt;     &lt;br /&gt;&amp;lt;goals&amp;gt;     &lt;br /&gt;&amp;lt;goal&amp;gt;repackage&amp;lt;/goal&amp;gt;     &lt;br /&gt;&amp;lt;/goals&amp;gt;     &lt;br /&gt;&amp;lt;/execution&amp;gt;     &lt;br /&gt;&amp;lt;/executions&amp;gt;     &lt;br /&gt;&amp;lt;/plugin&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;这几个模块最终会被打包成一个 springboot 的 jar 包，从而导致 integration-test 在编译时无法加载进来从而使用里面的类。&lt;/p&gt; &lt;p&gt;暂时没有找到好的解决办法，我就只有把这几个插件先去掉，需要打包时再手动指定插件。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;mvn clean package spring-boot:repackage -DskipTests=true     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;其实这里的本质问题也是没有分层的结果，最好还是依赖   &lt;code&gt;route&lt;/code&gt; 和   &lt;code&gt;server&lt;/code&gt; 的 SDK 进行测试。&lt;/p&gt; &lt;p&gt;现在因为有了测试的 CI 也欢迎大家来做贡献，可以看看这里的   &lt;code&gt;help want&lt;/code&gt;，有一些简单易上手可以先搞起来。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/09/06/kmgfrIxdhGXib9L.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;a href="https://github.com/crossoverJie/cim/issues/135"&gt;https://github.com/crossoverJie/cim/issues/135&lt;/a&gt;&lt;/p&gt; &lt;p&gt;参考链接：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;a href="https://github.com/crossoverJie/cim/pull/140"&gt;https://github.com/crossoverJie/cim/pull/140&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;   &lt;a href="https://github.com/crossoverJie/cim/pull/144"&gt;https://github.com/crossoverJie/cim/pull/144&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>cim test cim</category>
      <guid isPermaLink="true">https://itindex.net/detail/62942-%E5%A4%8D%E6%9D%82-java-%E5%BA%94%E7%94%A8</guid>
      <pubDate>Sun, 29 Sep 2024 11:37:13 CST</pubDate>
    </item>
    <item>
      <title>深入理解单元测试：技巧与最佳实践</title>
      <link>https://itindex.net/detail/62927-%E7%90%86%E8%A7%A3-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-%E6%8A%80%E5%B7%A7</link>
      <description>&lt;p&gt;之前分享过如何快速上手开源项目以及如何在开源项目里做集成测试，但还没有讲过具体的实操。&lt;/p&gt; &lt;p&gt;今天来详细讲讲如何写单元测试。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#20160;&amp;#20040;&amp;#24773;&amp;#20917;&amp;#19979;&amp;#38656;&amp;#35201;&amp;#21333;&amp;#20803;&amp;#27979;&amp;#35797;" title="&amp;#20160;&amp;#20040;&amp;#24773;&amp;#20917;&amp;#19979;&amp;#38656;&amp;#35201;&amp;#21333;&amp;#20803;&amp;#27979;&amp;#35797;"&gt;&lt;/a&gt;什么情况下需要单元测试&lt;/h1&gt; &lt;p&gt;这个大家应该是有共识的，对于一些功能单一、核心逻辑、同时变化不频繁的公开函数才有必要做单元测试。&lt;/p&gt; &lt;p&gt;对于业务复杂、链路繁琐但也是核心流程的功能通常建议做 e2e 测试，这样可以保证最终测试结果的一致性。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#20855;&amp;#20307;&amp;#26696;&amp;#20363;" title="&amp;#20855;&amp;#20307;&amp;#26696;&amp;#20363;"&gt;&lt;/a&gt;具体案例&lt;/h1&gt; &lt;p&gt;我们都知道单测的主要目的是模拟执行你写过的每一行代码，目的就是要覆盖到主要分支，做到自己的每一行代码都心中有数。&lt;/p&gt; &lt;p&gt;下面以   &lt;code&gt;Apache HertzBeat&lt;/code&gt; 的一些单测为例，讲解如何编写一个单元测试。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/SbqCvHVZk6f5tB1.png"&gt;&lt;/img&gt;  &lt;br /&gt;先以一个最简单的   &lt;code&gt;org.apache.hertzbeat.collector.collect.udp.UdpCollectImpl#preCheck&lt;/code&gt; 函数测试为例。  &lt;br /&gt;这里的   &lt;code&gt;preCheck&lt;/code&gt; 函数就是简单的检测做参数校验。  &lt;br /&gt;测试时只要我们手动将   &lt;code&gt;metrics&lt;/code&gt; 设置为   &lt;code&gt;null&lt;/code&gt; 就可以进入这个 if 条件。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@ExtendWith(MockitoExtension.class)     &lt;br /&gt;class UdpCollectImplTest {     &lt;br /&gt;     &lt;br /&gt;    @InjectMocks     &lt;br /&gt;    private UdpCollectImpl udpCollect;     &lt;br /&gt;     &lt;br /&gt;    @Test     &lt;br /&gt;    void testPreCheck() {     &lt;br /&gt;        List&amp;lt;String&amp;gt; aliasField = new ArrayList&amp;lt;&amp;gt;();     &lt;br /&gt;        aliasField.add(&amp;quot;responseTime&amp;quot;);     &lt;br /&gt;        Metrics metrics = new Metrics();     &lt;br /&gt;        metrics.setAliasFields(aliasField);     &lt;br /&gt;        assertThrows(IllegalArgumentException.class, () -&amp;gt; udpCollect.preCheck(metrics));     &lt;br /&gt;    }     &lt;br /&gt;}         &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;来看具体的单测代码，我们一行行的来看：&lt;/p&gt; &lt;p&gt;  &lt;code&gt;@ExtendWith(MockitoExtension.class)&lt;/code&gt; 是   &lt;code&gt;Junit5&lt;/code&gt; 提供的一个注解，里面传入的   &lt;code&gt;MockitoExtension.class&lt;/code&gt; 是我们单测 mock 常用的框架。&lt;/p&gt; &lt;p&gt;简单来说就是告诉   &lt;code&gt;Junit5&lt;/code&gt; ，当前的测试类会使用 mockito 作为扩展运行，从而可以   &lt;code&gt;mock&lt;/code&gt; 我们运行时的一些对象。&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@InjectMocks       &lt;br /&gt;private UdpCollectImpl udpCollect;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;@InjectMocks&lt;/code&gt; 也是   &lt;code&gt;mockito&lt;/code&gt; 这个库提供的注解，通常用于声明需要测试的类。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@InjectMocks       &lt;br /&gt;private AbstractCollect udpCollect;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/zSocET9f5y6lqC3.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;需要注意的是这个注解必须是一个具体的类，不可以是一个抽象类或者是接口。&lt;/p&gt; &lt;p&gt;其实当我们了解了他的原理就能知道具体的原因：  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/07/02/5DwRyVsBHd1EmpA.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;当我们 debug 运行时会发现   &lt;code&gt;udpCollect&lt;/code&gt; 对象是有值的，而如果我们去掉这个注解   &lt;code&gt;@InjectMocks&lt;/code&gt; 再运行就会抛空指针异常。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;因为并没有初始化 udpCollect&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;而使用   &lt;code&gt;@InjectMocks&lt;/code&gt;注解后，  &lt;code&gt;mockito&lt;/code&gt; 框架会自动给   &lt;code&gt;udpCollect&lt;/code&gt; 注入一个代理对象；而如果是一个接口或者是抽象类，mockito 框架是无法知道创建具体哪个对象。&lt;/p&gt; &lt;p&gt;当然在这个简单场景下，我们直接   &lt;code&gt;udpCollect = new UdpCollectImpl()&lt;/code&gt; 进行测试也是可以的。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#37197;&amp;#21512;-jacoco-&amp;#36755;&amp;#20986;&amp;#21333;&amp;#27979;&amp;#35206;&amp;#30422;&amp;#29575;" title="&amp;#37197;&amp;#21512; jacoco &amp;#36755;&amp;#20986;&amp;#21333;&amp;#27979;&amp;#35206;&amp;#30422;&amp;#29575;"&gt;&lt;/a&gt;配合 jacoco 输出单测覆盖率&lt;/h1&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/fgv3O4RbnHsQTWV.png"&gt;&lt;/img&gt;  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/07/02/coXOGkjyE2zKsYa.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;在 IDEA 中我们可以以   &lt;code&gt;Coverage&lt;/code&gt; 的方式运行，  &lt;code&gt;IDEA&lt;/code&gt; 就将我们的单测覆盖情况显示在源代码中，绿色的部分就代表在实际在运行时执行到的地方。&lt;/p&gt; &lt;p&gt;我们也可以在   &lt;code&gt;maven&lt;/code&gt; 项目中集成   &lt;code&gt;jacoco&lt;/code&gt;，只需要添加一个根目录的   &lt;code&gt;pom.xml&lt;/code&gt; 中添加一个   &lt;code&gt;plugin&lt;/code&gt; 就可以了。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;plugin&amp;gt;       &lt;br /&gt;    &amp;lt;groupId&amp;gt;org.jacoco&amp;lt;/groupId&amp;gt;       &lt;br /&gt;    &amp;lt;artifactId&amp;gt;jacoco-maven-plugin&amp;lt;/artifactId&amp;gt;       &lt;br /&gt;    &amp;lt;version&amp;gt;${jacoco-maven-plugin.version}&amp;lt;/version&amp;gt;       &lt;br /&gt;    &amp;lt;executions&amp;gt;       &lt;br /&gt;        &amp;lt;execution&amp;gt;       &lt;br /&gt;            &amp;lt;goals&amp;gt;       &lt;br /&gt;                &amp;lt;goal&amp;gt;prepare-agent&amp;lt;/goal&amp;gt;       &lt;br /&gt;            &amp;lt;/goals&amp;gt;       &lt;br /&gt;        &amp;lt;/execution&amp;gt;       &lt;br /&gt;        &amp;lt;execution&amp;gt;       &lt;br /&gt;            &amp;lt;id&amp;gt;report&amp;lt;/id&amp;gt;       &lt;br /&gt;            &amp;lt;phase&amp;gt;test&amp;lt;/phase&amp;gt;       &lt;br /&gt;            &amp;lt;goals&amp;gt;       &lt;br /&gt;                &amp;lt;goal&amp;gt;report&amp;lt;/goal&amp;gt;       &lt;br /&gt;            &amp;lt;/goals&amp;gt;       &lt;br /&gt;        &amp;lt;/execution&amp;gt;       &lt;br /&gt;    &amp;lt;/executions&amp;gt;       &lt;br /&gt;&amp;lt;/plugin&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;之后运行   &lt;code&gt;mvn test&lt;/code&gt; 就会在 target 目录下生成测试报告了。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/GiBrPjtLcXhgDbp.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;我们还可以在 GitHub 的 CI 中集成   &lt;code&gt;Codecov&lt;/code&gt;，他会直接读取 jacoco 的测试数据，并且在 PR 的评论区加上测试报告。  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/07/02/ujdGke4gf5mAW3x.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/nFt5SukAjMPZ96W.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/KcXJUs3mehxFAYz.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;需要从   &lt;code&gt;Codecov&lt;/code&gt; 里将你项目的 token 添加到 repo 的 环境变量中即可。&lt;/p&gt; &lt;p&gt;具体可以参考这个 PR：  &lt;a href="https://github.com/apache/hertzbeat/pull/1985"&gt;https://github.com/apache/hertzbeat/pull/1985&lt;/a&gt;&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#9728;&amp;#65039;&amp;#22797;&amp;#26434;&amp;#19968;&amp;#28857;&amp;#30340;&amp;#21333;&amp;#27979;" title="&amp;#9728;&amp;#65039;&amp;#22797;&amp;#26434;&amp;#19968;&amp;#28857;&amp;#30340;&amp;#21333;&amp;#27979;"&gt;&lt;/a&gt;☀️复杂一点的单测&lt;/h1&gt; &lt;p&gt;刚才展示的是一个非常简单的场景，下面来看看稍微复杂的。&lt;/p&gt; &lt;p&gt;我们以这个单测为例：  &lt;br /&gt;  &lt;code&gt;org.apache.hertzbeat.collector.collect.redis.RedisClusterCollectImplTest&lt;/code&gt;&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@ExtendWith(MockitoExtension.class)     &lt;br /&gt;public class RedisClusterCollectImplTest {     &lt;br /&gt;         &lt;br /&gt;    @InjectMocks     &lt;br /&gt;    private RedisCommonCollectImpl redisClusterCollect;     &lt;br /&gt;     &lt;br /&gt;     &lt;br /&gt;    @Mock     &lt;br /&gt;    private StatefulRedisClusterConnection&amp;lt;String, String&amp;gt; connection;     &lt;br /&gt;     &lt;br /&gt;    @Mock     &lt;br /&gt;    private RedisAdvancedClusterCommands&amp;lt;String, String&amp;gt; cmd;     &lt;br /&gt;     &lt;br /&gt;    @Mock     &lt;br /&gt;    private RedisClusterClient client;     &lt;br /&gt;}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;这个单测在刚才的基础上多了一个   &lt;code&gt;@Mock&lt;/code&gt; 的注解。&lt;/p&gt; &lt;p&gt;这是因为我们需要测试的   &lt;code&gt;RedisCommonCollectImpl&lt;/code&gt; 类中需要依赖   &lt;code&gt;StatefulRedisClusterConnection/RedisAdvancedClusterCommands/RedisClusterClient&lt;/code&gt; 这几个类所提供的服务。&lt;/p&gt; &lt;p&gt;单测的时候需要使用   &lt;code&gt;mockito&lt;/code&gt; 创建一个他们的对象，并且注入到需要被测试的   &lt;code&gt;RedisCommonCollectImpl&lt;/code&gt;类中。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;不然我们就需要准备单测所需要的资源，比如可以使用的 Redis、MySQL 等。&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;  &lt;a href="http://crossoverjie.top/#&amp;#27169;&amp;#25311;&amp;#34892;&amp;#20026;" title="&amp;#27169;&amp;#25311;&amp;#34892;&amp;#20026;"&gt;&lt;/a&gt;模拟行为&lt;/h2&gt; &lt;p&gt;只是注入进去还不够，我们还需要模拟它的行为：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;比如调用某个函数可以模拟返回数据&lt;/li&gt;  &lt;li&gt;模拟函数调用抛出异常&lt;/li&gt;  &lt;li&gt;模拟函数调用耗时&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;这里以最常见的模拟函数返回为例：&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/3lnFxsQmcWqao5u.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;String clusterNodes = connection.sync().clusterInfo();     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;在源码里看到会使用 connection 的   &lt;code&gt;clusterInfo()&lt;/code&gt; 函数返回集群信息。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;String clusterKnownNodes = &amp;quot;2&amp;quot;;     &lt;br /&gt;String clusterInfoTemp = &amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;        cluster_slots_fail:0     &lt;br /&gt;        cluster_known_nodes:%s     &lt;br /&gt;        &amp;quot;&amp;quot;&amp;quot;;     &lt;br /&gt;String clusterInfo = String.format(clusterInfoTemp, clusterKnownNodes);     &lt;br /&gt;Mockito.when(cmd.clusterInfo()).thenReturn(clusterInfo);             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;此时我们就可以使用   &lt;code&gt;Mockito.when().thenReturn()&lt;/code&gt; 来模拟这个函数的返回数据。&lt;/p&gt; &lt;p&gt;而其中的   &lt;code&gt;cmd&lt;/code&gt; 自然也是需要模拟返回的：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;Mockito.mockStatic(RedisClusterClient.class).when(()-&amp;gt;RedisClusterClient.create(Mockito.any(ClientResources.class),     &lt;br /&gt;        Mockito.any(RedisURI.class))).thenReturn(client);     &lt;br /&gt;Mockito.when(client.connect()).thenReturn(connection);     &lt;br /&gt;     &lt;br /&gt;Mockito.when(connection.sync()).thenReturn(cmd);     &lt;br /&gt;Mockito.when(cmd.info(metrics.getName())).thenReturn(info);     &lt;br /&gt;Mockito.when(cmd.clusterInfo()).thenReturn(clusterInfo);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;code&gt;cmd&lt;/code&gt; 是通过   &lt;code&gt;Mockito.when(connection.sync()).thenReturn(cmd);&lt;/code&gt;返回的，而   &lt;code&gt;connection&lt;/code&gt; 又是从   &lt;code&gt;client.connect()&lt;/code&gt; 返回的。&lt;/p&gt; &lt;p&gt;最终就像是套娃一样，  &lt;code&gt;client&lt;/code&gt; 在源码中是通过一个静态函数创建的。&lt;/p&gt; &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#9889;&amp;#27169;&amp;#25311;&amp;#38745;&amp;#24577;&amp;#20989;&amp;#25968;" title="&amp;#9889;&amp;#27169;&amp;#25311;&amp;#38745;&amp;#24577;&amp;#20989;&amp;#25968;"&gt;&lt;/a&gt;⚡模拟静态函数&lt;/h3&gt; &lt;p&gt;我依稀记得在我刚接触   &lt;code&gt;mockito&lt;/code&gt; 的 16～17 年那段时间还不支持模拟调用静态函数，不过如今已经支持了：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;@Mock       &lt;br /&gt;private RedisClusterClient client;     &lt;br /&gt;     &lt;br /&gt;     &lt;br /&gt;Mockito.mockStatic(RedisClusterClient.class).when(()-&amp;gt;RedisClusterClient.create(Mockito.any(ClientResources.class),       &lt;br /&gt;        Mockito.any(RedisURI.class))).thenReturn(client);     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;这样就可以模拟静态函数的返回值了，但前提是返回的   &lt;code&gt;client&lt;/code&gt; 需要使用   &lt;code&gt;@Mock&lt;/code&gt; 注解。&lt;/p&gt; &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#27169;&amp;#25311;&amp;#26500;&amp;#36896;&amp;#20989;&amp;#25968;" title="&amp;#27169;&amp;#25311;&amp;#26500;&amp;#36896;&amp;#20989;&amp;#25968;"&gt;&lt;/a&gt;模拟构造函数&lt;/h3&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/aFiCLRyYh4IU83o.png"&gt;&lt;/img&gt;  &lt;br /&gt;有时候我们也需要模拟构造函数，从而可以模拟后续这个对象的行为。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;MockedConstruction&amp;lt;FTPClient&amp;gt; mocked = Mockito.mockConstruction(FTPClient.class,     &lt;br /&gt;        (ftpClient, context) -&amp;gt; {     &lt;br /&gt;            Mockito.doNothing().when(ftpClient).connect(ftpProtocol.getHost(),     &lt;br /&gt;                    Integer.parseInt(ftpProtocol.getPort()));     &lt;br /&gt;     &lt;br /&gt;            Mockito.doAnswer(invocationOnMock -&amp;gt; true).when(ftpClient)     &lt;br /&gt;                    .login(ftpProtocol.getUsername(), ftpProtocol.getPassword());     &lt;br /&gt;            Mockito.when(ftpClient.changeWorkingDirectory(ftpProtocol.getDirection())).thenReturn(isActive);     &lt;br /&gt;            Mockito.doNothing().when(ftpClient).disconnect();     &lt;br /&gt;        });     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;可以使用   &lt;code&gt;Mockito.mockConstruction&lt;/code&gt; 来进行模拟，该对象的一些行为就直接写在这个模拟函数内。&lt;/p&gt; &lt;p&gt;需要注意的是返回的   &lt;code&gt;mocked&lt;/code&gt; 对象需要记得关闭。&lt;/p&gt; &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#19981;&amp;#38656;&amp;#35201;-Mock" title="&amp;#19981;&amp;#38656;&amp;#35201; Mock"&gt;&lt;/a&gt;不需要 Mock&lt;/h3&gt; &lt;p&gt;当然也不是所有的场景都需要   &lt;code&gt;mock&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;比如刚才第一个场景，没有依赖任何外部服务时就不需要   &lt;code&gt;mock&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/02/mW3gxERBc4qoz2L.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;类似于这个   &lt;a href="https://github.com/apache/hertzbeat/pull/2021"&gt;PR&lt;/a&gt; 里的测试，只是依赖一个基础的内存缓存组件，就没必要 mock，但如果依赖的是   &lt;code&gt;Redis&lt;/code&gt; 缓存组件还是需要 mock 的。  &lt;br /&gt;  &lt;a href="https://github.com/apache/hertzbeat/pull/2021"&gt;https://github.com/apache/hertzbeat/pull/2021&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;  &lt;a href="http://crossoverjie.top/#&amp;#9881;&amp;#65039;&amp;#20462;&amp;#25913;&amp;#28304;&amp;#30721;" title="&amp;#9881;&amp;#65039;&amp;#20462;&amp;#25913;&amp;#28304;&amp;#30721;"&gt;&lt;/a&gt;⚙️修改源码&lt;/h3&gt; &lt;p&gt;如果有些测试场景下需要获取内部变量方便后续的测试，但是该测试类也没有提供获取变量的函数，我们就只有修改源码来配合测试了。&lt;/p&gt; &lt;p&gt;比如这个   &lt;a href="https://github.com/apache/hertzbeat/pull/"&gt;PR&lt;/a&gt;：  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/07/02/bxfQgsymWVcnawE.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;当然如果只是给测试环境下使用的函数或变量，我们可以加上   &lt;code&gt;@VisibleForTesting&lt;/code&gt;注解标明一下，这个注解没有其他作用，可以让后续的维护者更清楚的知道这是做什么用的。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;" title="&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;"&gt;&lt;/a&gt;集成测试&lt;/h1&gt; &lt;p&gt;单元测试只能测试一些功能单一的函数，要保证整个软件的质量仅依赖单测是不够的，我们还需要集成测试。&lt;/p&gt; &lt;p&gt;通常是需要对外提供服务的开源项目都需要集成测试：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;Pulsar&lt;/li&gt;  &lt;li&gt;Kafka&lt;/li&gt;  &lt;li&gt;Dubbo 等&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;以我接触到的服务型应用主要分为两类：一个是 Java 应用一个是 Golang 应用。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#Golang" title="Golang"&gt;&lt;/a&gt;Golang&lt;/h1&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/11/vZISu9Qg3foKhsU.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Golang&lt;/code&gt; 因为工具链没有 Java 那么强大，所以大部分的集成测试的功能都是通过编写 Makefile 和 shell 脚本实现的。&lt;/p&gt; &lt;p&gt;还是以我熟悉的 Pulsar 的   &lt;code&gt;go-client&lt;/code&gt; 为例，它在 GitHub 的集成测试是通过 GitHub action 触发的，定义如下：  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/05/20/f2196pujo8m7KRe.png"&gt;&lt;/img&gt;  &lt;br /&gt;最终调用的是 Makefile 中的 test 命令，并且把需要测试的 Golang 版本传入进去。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/05/20/YpwtSHnLXqU1xQj.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Dockerfile&lt;/code&gt;：  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/05/20/1ySGWF46U7EC2rk.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;这个镜像简单来说就是将 Pulsar 的镜像作为基础运行镜像（这里面包含了 Pulsar 的服务端），然后将这个 pulsar-client-go 的代码复制进去编译。&lt;/p&gt; &lt;p&gt;接着运行：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;cd /pulsar/pulsar-client-go &amp;amp;&amp;amp; ./scripts/run-ci.sh     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;也就是测试脚本。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/05/20/2Afmdu8ozRvH9FC.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;测试脚本的逻辑也很简单：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;启动 pulsar 服务端&lt;/li&gt;  &lt;li&gt;运行测试代码   &lt;br /&gt;因为所有的测试代码里连接服务端的地址都是    &lt;code&gt;localhost&lt;/code&gt;，所以可以直接连接。   &lt;br /&gt;   &lt;img src="https://s2.loli.net/2024/05/20/C1RHxTkuz25Mlj8.png"&gt;&lt;/img&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;通过这里的   &lt;a href="https://github.com/apache/pulsar-client-go/actions/runs/9014510238/job/24768797555"&gt;action&lt;/a&gt; 日志可以跟踪所有的运行情况。&lt;/p&gt; &lt;h1&gt;  &lt;a href="http://crossoverjie.top/#&amp;#9749;Java" title="&amp;#9749;Java"&gt;&lt;/a&gt;☕Java&lt;/h1&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/07/11/KlqzSwJ6f895A4n.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;Java 因为工具链强大，所以集成测试几乎不需要用 Makefile 和脚本配合执行。&lt;/p&gt; &lt;p&gt;还是以 Pulsar 为例，它的集成测试是需要模拟在本地启动一个服务端（因为 Pulsar 的服务端源码和测试代码都是 Java 写的，更方便做测试），然后再运行测试代码。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;这个的好处是任何一个单测都可以在本地直接运行，而  Go 的代码还需要先在本地启动一个服务端，测试起来比较麻烦。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;来看看它是如何实现的，我以其中一个   &lt;a href="https://github.com/apache/pulsar/blob/631b13ad23d7e48c6e82d38f97c23d129062cb7c/pulsar-broker/src/test/java/org/apache/pulsar/client/impl/BrokerClientIntegrationTest.java#L117"&gt;BrokerClientIntegrationTest&lt;/a&gt;为例：  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/05/20/9PbioA3RQLMBy6J.png"&gt;&lt;/img&gt;  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/05/20/blKePdxTUIkgRD3.png"&gt;&lt;/img&gt;  &lt;br /&gt;会在单测启动的时候先启动服务端。&lt;/p&gt; &lt;p&gt;  &lt;img src="https://s2.loli.net/2024/05/20/gzY3lyTGuEDUwZF.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;最终会调用   &lt;code&gt;PulsarTestContext&lt;/code&gt; 的   &lt;code&gt;build&lt;/code&gt; 函数启动   &lt;code&gt;broker&lt;/code&gt;（服务端），而执行单测也只需要使用   &lt;code&gt;mvn test&lt;/code&gt; 就可以自动触发这些单元测试。  &lt;br /&gt;  &lt;img src="https://s2.loli.net/2024/05/20/N15amZihWI73Qyw.png"&gt;&lt;/img&gt;  &lt;br /&gt;只是每一个单测都需要启停服务端，所以要把 Pulsar 的所有单测跑完通常需要 1～2 个小时。&lt;/p&gt; &lt;p&gt;以上就是日常编写单测可能会碰到的场景，希望对大家有所帮助。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>OB 单测</category>
      <guid isPermaLink="true">https://itindex.net/detail/62927-%E7%90%86%E8%A7%A3-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-%E6%8A%80%E5%B7%A7</guid>
      <pubDate>Thu, 15 Aug 2024 01:28:12 CST</pubDate>
    </item>
    <item>
      <title>MBTI性格测试像星座运势一样不靠谱</title>
      <link>https://itindex.net/detail/62907-mbti-%E6%80%A7%E6%A0%BC-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;div&gt;    &lt;p&gt;〔按〕昨日年会，有同事在群里推荐MBTI测试，于是我翻出这篇科普旧作。五年多来，对MBTI的追捧愈演愈烈。当年《南方周末》编辑约稿写专栏，每篇2千字篇幅，我则螺蛳壳里做道场，把1万字的信息容量精心收纳到2千字的空间里，并在通俗性和严谨性之间保持平衡，着实要下很大一番工夫，工匠般精雕细琢的结果就是至今读来无一句废话，无一字多余。我引以为豪。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;说MBTI（Myers–Briggs Type Indicator）是全世界最流行的性格测试一点也不夸张。无论是考大学选专业还是大学毕业选职业，都会用它作为参考，甚至有人把MBTI类型是否匹配列为找对象的标准之一。如今，许多高校都会在就业指导课上向学生讲授这一测试，《财富》世界五百强中有超过八成的企业使用MBTI作为人力资源测评的工具，桥水基金创始人Ray Dalio在新作《原则》中也肯定了MBTI在公司管理中的作用，就连顶级科学期刊《自然》都曾撰文向科研机构推荐过它。¹&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;与上述情况形成鲜明对比的是，作为一项心理测试，MBTI在心理学界乏人问津。心理学教科书和课堂上几乎不会提到它，如果提到也是作为反面教材。²心理学期刊中很少会看到针对它的研究，倒是每隔几年，心理学家就会全面总结一下最新的研究成果，指出MBTI存在的种种问题。主流共识是，MBTI是一项不靠谱的性格测试。³⁻⁶&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;MBTI的不靠谱源于它的先天不足。其理论基础出自心理学家荣格，经由身为心理学爱好者的一对母女Briggs和Myers扩展完善，将人的性格从四个维度进行划分，组合成16种不同的性格类型。MBTI是心理学还未发展成为一门实证科学之前的产物，无论荣格还是那对母女，他们对性格的分类主要源自个体经验的构建。这样建造出的房屋再精致诱人，也注定是一座空中楼阁。研究表明，其中有两个维度存在相关性，可谓叠床架屋。⁷&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;MBTI在构建中最大的败笔是把性格进行了非此即彼的二分。² ⁸比如人类性格的内外向维度，MBTI会根据得分，把你划入内向型或外向型。这意味着，全体人类中的内向者和外向者会分别聚集在两端，形成统计学上的双峰分布（M形）。事实上，这个维度应该是从内向到外向的一个连续型正态分布（倒V形），少数人非常内向或非常外向，多数人则介于两者之间。备受心理学界推崇的“大五”性格测试，正符合后一种情形，它基于实证数据、经由统计学分析，把人的性格提炼出五个不同维度，每个维度都呈连续型分布。⁹¹⁰&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;即便存在上述先天不足，仍有许多MBTI的支持者声称它是靠谱的。这就需要通过考察两大指标来加以驳斥。其一是信度。好比用一把尺子来测量身高，上周测的结果和这周测的结果相同，说明这把尺子的信度高，反之则信度低。常常听人说到自己先后测过几次MBTI，每次结果都不相同，这正是信度低的表现。有研究表明，在仅仅相隔五周之后重测，超过一半人的性格类型从一种变成了另外一种。¹¹性格当然不可能如此善变，很多人其实处于某一性格维度（比如内外向）的中间地带，MBTI得分上出现少许变化，就由内向型突变成了外向型。⁸&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;如果把强行二分的做法改成直接比较得分，MBTI的信度会提高不少。¹²但光有信度是不够的，在此基础上要考察第二个指标，效度。尺子用来测身高很合适，拿去测体重却是无效的，毕竟同样身高的人体重可以差很多。MBTI就是这样一把无效的尺子，它在各方面的效度都不尽如人意。MBTI广受欢迎的一个原因，是大家认为它可以用于指导就业，不同MBTI类型的人适合不同的工作，以及哪些类型的人适合在一起工作。这些指的其实是效度最重要的一个方面，即预测效度，测量一个人的MBTI类型可以预测他与工作相关的表现。而研究表明，MBTI既不能预测一个人会选择什么工作，也不能预测他对工作的满意度，更不能预测领导者的管理效能以及工作团队的绩效。¹²⁻¹⁴&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;相较之下，无论是前述“大五”性格测试，还是基于“大五”理论、用于就业指导的霍根性格调查问卷，¹⁵都有着良好的信效度，为心理学界所公认。遗憾的是，它们在大众中的流行程度远不及MBTI，大家总是喜欢那些含义模糊却积极向上的性格剖析，认为它准确地描述了自己，MBTI正好迎合了这种大众心理，心理学上将其称作“巴纳姆效应”。⁶ ⁸&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;MBTI及其衍生的凯尔西气质类型调查表的测试结果，对每种性格类型，先来一段正面的特征描述，再附上几种体面的参考职业，甚至给出一个名人作为该类型代表（比如INTP类型的代表人物是爱因斯坦），这样对自己性格的解读谁会不喜欢呢？根据MBTI理论，小偷和乞丐、希特勒或孔乙己想必也对应某种性格类型，怎么不把这些写进来呢？&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;其实我们大可以把MBTI当成像星座运势那样的谈资，作为一种活跃气氛的娱乐话题无可厚非。但是它不值得个人、学校和企业去投入大量资金和精力，花冤枉钱不说，做的还是无用功。Ray Dalio在《原则》中讲到，他让公司管理层做了MBTI测试，他觉得测试结果与自己对这些人的了解大相径庭，但是当他要求这些人评价测试结果的准确性时，多数人都认可了自己的测试结果。这个故事既反映了MBTI的效度有问题，又体现了普遍存在的巴纳姆效应。遗憾的是，聪明如Dalio者，面对上述情形，不是去反思MBTI是否有效，而是削足适履地选择了用MBTI测试的结果来重新审视身边的同事。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;总之，MBTI不仅指导不了个人的就业和企业的绩效，更预测不了个人的命运和企业的前途。不幸的是，它已经成为了社会文化的一部分，更作为一个产业牵动着许多人的利益。就连MBTI测试的出版公司CPP的董事会主席、资深心理学家Carl Thoresen也承认：“我没有在任何研究中使用过它，这一定程度上是因为，如果用了它，学术界同行会质疑我。”这里不讲科学与证据，谈的都是情怀和生意。&lt;/p&gt;    &lt;br /&gt;〔参考文献〕    &lt;ol&gt;      &lt;li&gt;Loc, C. (2012). What’s your type?         &lt;em&gt;Nature&lt;/em&gt;, 488(7412), 545-547.        &lt;br /&gt;&lt;/li&gt;      &lt;li&gt;Larsen, R. J., &amp;amp; Buss, D. M. (2018).         &lt;em&gt;Personality psychology&lt;/em&gt;. Boston: McGraw-Hill.&lt;/li&gt;      &lt;li&gt;Boyle, G. J. (1995). Myers‐Briggs Type Indicator (MBTI): Some psychometric limitations.         &lt;em&gt;Australian Psychologist&lt;/em&gt;, 30(1), 71-74.&lt;/li&gt;      &lt;li&gt;Hunsley, J., Lee, C. M., &amp;amp; Wood, J. M. (2003). Controversial and questionable assessment techniques. In S. O. Lilienfeld, S. J. Lynn, &amp;amp; J. M. Lohr (Eds.),         &lt;em&gt;Science and pseudoscience in clinical psychology&lt;/em&gt; (pp. 39-76). New York: Guilford Press.&lt;/li&gt;      &lt;li&gt;Pittenger, D. J. (2005). Cautionary comments regarding the Myers-Briggs Type Indicator.         &lt;em&gt;Consulting Psychology Journal: Practice and Research&lt;/em&gt;, 57(3), 210-221.&lt;/li&gt;      &lt;li&gt;Gerras, S. J., &amp;amp; Wong, L. (2016). Moving beyond the MBTI: The Big Five and leader development.         &lt;em&gt;Military Review&lt;/em&gt;, 96(2), 54-57.&lt;/li&gt;      &lt;li&gt;McCrae, R. R., &amp;amp; Costa, P. T. (1989). Reinterpreting the Myers‐Briggs Type Indicator from the perspective of the five‐factor model of personality.         &lt;em&gt;Journal of Personality&lt;/em&gt;, 57(1), 17-40.&lt;/li&gt;      &lt;li&gt;Pittenger, D. J. (1993). Measuring the MBTI… and coming up short.         &lt;em&gt;Journal of Career Planning and Employment&lt;/em&gt;, 54(1), 48-52.&lt;/li&gt;      &lt;li&gt;Barrick, M. R., &amp;amp; Mount, M. K. (1991). The Big Five personality dimensions and job performance: A meta‐analysis.         &lt;em&gt;Personnel Psychology&lt;/em&gt;, 44(1), 1-26.&lt;/li&gt;      &lt;li&gt;Goldberg, L. R. (1990). An alternative “description of personality”: The Big-Five factor structure.         &lt;em&gt;Journal of Personality and Social Psychology&lt;/em&gt;, 59(6), 1216-1229.&lt;/li&gt;      &lt;li&gt;McCarley, N. G., &amp;amp; Carskadon, T. G. (1983). Test-retest reliabilities of scales and subscales of the Myers-Briggs Type Indicator and of criteria for clinical interpretive hypotheses involving them.         &lt;em&gt;Research in Psychological Type&lt;/em&gt;, 6, 24-36.&lt;/li&gt;      &lt;li&gt;Gardner, W. L., &amp;amp; Martinko, M. J. (1996). Using the Myers-Briggs Type Indicator to study managers: A literature review and research agenda.         &lt;em&gt;Journal of Management&lt;/em&gt;, 22(1), 45-83.&lt;/li&gt;      &lt;li&gt;Kuipers, B. S., Higgs, M. J., Tolkacheva, N. V., &amp;amp; de Witte, M. C. (2009). The influence of Myers-Briggs Type Indicator profiles on team development processes: An empirical study in the manufacturing industry.         &lt;em&gt;Small Group Research&lt;/em&gt;, 40(4), 436-464.&lt;/li&gt;      &lt;li&gt;National Research Council. (1991).         &lt;em&gt;In the mind&amp;apos;s eye: Enhancing human performance&lt;/em&gt;. Washington, DC: National Academies Press.&lt;/li&gt;      &lt;li&gt;Hogan, J., &amp;amp; Holland, B. (2003). Using theory to evaluate personality and job-performance relations: A socioanalytic perspective.         &lt;em&gt;Journal of Applied Psychology&lt;/em&gt;, 88(1), 100-112.&lt;/li&gt;&lt;/ol&gt;    &lt;p&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62907-mbti-%E6%80%A7%E6%A0%BC-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Sun, 21 Jan 2024 20:52:31 CST</pubDate>
    </item>
    <item>
      <title>Meta开源的ChatGPT平替到底好不好用？测试结果、加料改装方法已出炉，2天5.2k星</title>
      <link>https://itindex.net/detail/62659-meta-%E5%BC%80%E6%BA%90-chatgpt</link>
      <description>&lt;section&gt;ChatGPT 的持续爆火，早已让各大科技公司坐不住了。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;就在刚刚过去的一周，Meta「开源」了一个新的大模型系列 &amp;mdash;&amp;mdash;&lt;a data-itemshowtype="0" data-linktype="2" href="http://mp.weixin.qq.com/s?__biz=MzA3MzI4MjgzMw==&amp;mid=2650869478&amp;idx=1&amp;sn=c06afe59ab0322e885a0f4358b9b6907&amp;chksm=84e4ca98b393438e77ff7893e43524273e396e1a0c43fae592b04acfb674ab8f64ffb2ba21ae&amp;scene=21#wechat_redirect" target="_blank"&gt;LLaMA&lt;/a&gt;（Large Language Model Meta AI），&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;量从 70 亿到 650 亿不等。因为 LLaMA 比之前发布的很多大模型&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;更少，但性能更好，所以一经发布让很多研究者兴奋不已。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;例如，130 亿&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;的 LLaMA 模型「在大多数&lt;mark data-type=concepts data-id=308c3a45-0fee-4ec6-858e-85b15f440fc0&gt;基准&lt;/mark&gt;上」可以胜过&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;量达 1750 亿的 GPT-3，而且可以在单块 V100 GPU 上运行；而最大的 650 亿&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;的 LLaMA 模型可以媲美谷歌的 Chinchilla-70B 和 PaLM-540B。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;mark data-type=concepts data-id=2e982b73-88e2-41e8-a430-f7ae5a9af4bf&gt;参数&lt;/mark&gt;量的减少对于普通研究者和商业机构来说都是好事，但 LLaMA 真的像论文中说得那样表现那么好吗？和当前的 ChatGPT 相比，LLaMA 是否可以勉强一战？为了解答这些疑问，有些研究者已经对这一模型进行了测试。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;还有公司已经在尝试补齐 LLaMA 短板，想看能不能通过添加 RLHF 等训练方法让 LLaMA 表现更好。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;strong&gt;LLaMA 初步评测&lt;/strong&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;这份评测结果来自一位名叫 @Enryu 的 Medium 作者。它比较了 LLaMA 和 ChatGPT 在解释笑话、零样本分类和代码生成三个颇具挑战性的任务中的效果。相关博客文章为《Mini-post: first look at LLaMA》。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;作者在 RTX 3090/RTX 4090 上运行 LLaMA 7B/13B 版本，在单个 A100 上运行 33B 版本。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;需要注意的是，与 ChatGPT 不同，其他模型并不是基于指令微调，因此 prompt 的结构有所不同。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;strong&gt;解释笑话&lt;/strong&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;这是谷歌原始 PaLM 论文中展示的一个用例：给出一个笑话，让模型来解释它为什么好笑。该任务需要将世界知识和一些基本&lt;mark data-type=concepts data-id=95a97f4b-79d2-4bbc-91ae-300f074dff9f&gt;逻辑&lt;/mark&gt;相结合。PaLM 之前的所有模型都无法做到这一点。作者从 PaLM 论文中提取了一些示例，比较了 LLaMA-7B、LLaMA-13B、LLaMA-33B 与 ChatGPT 的表现。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="0.18823529411764706" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMUnIGDVflgib8uDljkrpGenB6NYx1CuCZYV001gPBbSSicjSzZXDBprPicw/640?wx_fmt=png" data-type="png" data-w="1360" data-index="1" src="https://image.jiqizhixin.com/uploads/editor/d1ffa829-b3ba-47e4-8a26-97fb9b0296be/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;可以看到，结果很糟糕。这些模型 get 到了一些笑点，但无法真正理解，它们只是随机生成一些相关的文本流。ChatGPT 虽与 LLaMA-33B 一样表现很差（其他几个模型更差），但它遵循了不一样的策略：生成了一大堆文本，希望自己的回答至少有一部分是正确的（但大部分显然不是），是不是很像大家考试时应对问答题的策略？&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;不过，ChatGPT 起码 get 到了关于 Schmidthuber 的笑话。但总的来说，这些模型在零样本笑话解释任务上的效果与 PaLM 相差甚远（除非 PaLM 的示例是精心挑选）。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;strong&gt;零样本分类&lt;/strong&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;作者考虑的第二项任务更具挑战性 &amp;mdash;&amp;mdash; 标题党（clickbait）分类。由于连人类也无法就什么是标题党达成一致，作者在 prompt 中为这些模型提供了一些示例（因此实际上是小样本而非零样本）。如下为 LLaMa 的 prompt：&lt;/section&gt;&lt;p&gt;&lt;img src="https://image.jiqizhixin.com/uploads/editor/0a4c0fc0-cdff-4de8-b06a-ed217c588662/1677993296430.png" style="width: 700%;" class="fr-fic fr-dib"&gt;&lt;/p&gt;&lt;section&gt;下图为 LLaMA-7B、LLaMA-13B、LLaMA-33B 与 ChatGPT 的更多示例结果。&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="0.2039695945945946" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMUqXyfu7YW71wPNibR1RyfDUSrSibJHriat7jBJZ5exd7iaV5B8LrpOvSvibQ/640?wx_fmt=png" data-type="png" data-w="2368" data-index="2" src="https://image.jiqizhixin.com/uploads/editor/5be53f2c-2969-448d-8729-f01cbc1308c4/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;很明显，赢家为 LLaMA-33B，它是唯一一个能够遵循所有请求格式（yes/no）的模型，并且预测合理。ChatGPT 也还可以，但有些预测不太合理，格式也有错误。较小的模型（7B/13B）不适用于该任务。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;strong&gt;代码生成&lt;/strong&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;虽然 LLM 擅长人文学科，但在 STEM 学科上表现糟糕。LLaMA 虽然有&lt;mark data-type=concepts data-id=308c3a45-0fee-4ec6-858e-85b15f440fc0&gt;基准&lt;/mark&gt;测试结果，但作者在代码生成领域尝试了一些特别的东西，即将人类语言零样本地转换为 SQL &lt;mark data-type=concepts data-id=bf740558-f0f7-41a8-87a0-e695a97563b3&gt;查询&lt;/mark&gt;。这并不是很实用，在现实生活中直接编写&lt;mark data-type=concepts data-id=bf740558-f0f7-41a8-87a0-e695a97563b3&gt;查询&lt;/mark&gt;会更有效率。这里只作为代码生成任务的一个示例。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;在 prompt 中，作者提供表模式（table schema）以及想要实现的目标，要求模型给出 SQL &lt;mark data-type=concepts data-id=bf740558-f0f7-41a8-87a0-e695a97563b3&gt;查询&lt;/mark&gt;。&lt;/section&gt;&lt;section&gt;从测试结果来看，LLaMA 在一些任务上表现还不错，但在另一些任务上和 ChatGPT 还有一些差距。如果能像 ChatGPT 一样加入一些「训练秘籍」，效果会不会大幅提升？&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;strong&gt;加入 RLHF，初创公司 Nebuly AI 开源 ChatLLaMA 训练方法&lt;/strong&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;虽然 LLaMA 发布之初就得到众多研究者的青睐，但是少了 RLHF 的加持，从上述评测结果来看，还是差点意思。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;在 LLaMA 发布三天后，初创公司 Nebuly AI 开源了 RLHF 版 LLaMA（ChatLLaMA）的训练方法。它的训练过程类似 ChatGPT，该项目允许基于预训练的 LLaMA 模型构建 ChatGPT 形式的服务。项目上线刚刚 2 天，狂揽 5.2K 星。&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="0.3348104382077794" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMUv9XDtprNjG4Q7zvYcsgZAEb4qeKc3QEVVzLpLhCtp8N5eG1rnUaAWA/640?wx_fmt=png" data-type="png" data-w="2031" data-index="3" src="https://image.jiqizhixin.com/uploads/editor/98730d6a-a6cf-4ab1-8aca-66926df7f3cc/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;项目地址：https://github.com/nebuly-ai/nebullvm/tree/main/apps/accelerate/chatllama&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;ChatLLaMA 训练过程算法实现主打比 ChatGPT 训练更快、更便宜，我们可以从以下四点得到验证：&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;ul&gt;&lt;li&gt;&lt;section&gt;ChatLLaMA 是一个完整的开源实现，允许用户基于预训练的 LLaMA 模型构建 ChatGPT 风格的服务；&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section&gt;与 ChatGPT 相比，LLaMA 架构更小，但训练过程和单 GPU 推理速度更快，成本更低；&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section&gt;ChatLLaMA 内置了对 DeepSpeed ZERO 的支持，以加速微调过程；&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section&gt;该库还支持所有的 LLaMA 模型架构（7B、13B、33B、65B），因此用户可以根据训练时间和推理性能偏好对模型进行微调。&lt;/section&gt;&lt;/li&gt;&lt;/ul&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="0.5833333333333334" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMU8S8fvBic1YJbZM2icC0rC5NjuwnSmYp3mYxGIVYmSibmUibQxFDePLEcNQ/640?wx_fmt=png" data-type="png" data-w="2064" data-index="4" src="https://image.jiqizhixin.com/uploads/editor/0c51a6c5-5c8c-4a96-96ad-d80e73b841b0/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;&lt;em&gt;图源：https://openai.com/blog/chatgpt&lt;/em&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;更是有研究者表示，ChatLLaMA 比 ChatGPT 训练速度最高快 15 倍。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="1.124236252545825" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMUISTVibUUwicJ4xvgfGeWoCczIOiaAhV7wQaW9MO8PeL02YzSVYoW9yTqA/640?wx_fmt=png" data-type="png" data-w="982" data-index="5" src="https://image.jiqizhixin.com/uploads/editor/daa743a3-bc2f-49e3-a7b3-a3036dd7ea3d/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;不过有人对这一说法提出质疑，认为该项目没有给出准确的衡量标准。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;img data-ratio="0.1782312925170068" data-src="https://mmbiz.qpic.cn/mmbiz_png/KmXPKA19gWicHsGdj258fyg3aujOh9lMUdGrcg6u8SZBxAoCyjCl7Rh4SS3Y6ib9OO3JcrRso35lJWVdwnCLEKdg/640?wx_fmt=png" data-type="png" data-w="735" data-index="6" src="https://image.jiqizhixin.com/uploads/editor/e37f35b4-5a06-4b0b-aebb-a54f03fdb8ff/640.png" alt="图片" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;section&gt;项目刚刚上线 2 天，还处于早期阶段，用户可以通过以下添加项进一步扩展：&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;ul&gt;&lt;li&gt;&lt;section&gt;带有微调&lt;mark data-type=concepts data-id=149a12cf-10c2-4555-9899-cc6dee319ef5&gt;权重&lt;/mark&gt;的 Checkpoint；&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section&gt;用于快速推理的优化技术；&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section&gt;支持将模型打包到有效的部署框架中。&lt;/section&gt;&lt;/li&gt;&lt;/ul&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;Nebuly AI 希望更多人加入进来，创造更高效和开放的 ChatGPT 类助手。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;该如何使用呢？首先是使用 pip 安装软件包：&lt;/section&gt;&lt;p&gt;&lt;img src="https://image.jiqizhixin.com/uploads/editor/b6fff319-4932-4b9d-966f-914ac9433f5b/1677993339601.png" style="width: 65.14%;" class="fr-fic fr-dib"&gt;&lt;/p&gt;&lt;p&gt;然后是克隆 LLaMA 模型：&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;img src="https://image.jiqizhixin.com/uploads/editor/23adaf09-e7fb-4b05-8f1a-aa9a513da6ed/1677993356917.png" style="width: 700%;" class="fr-fic fr-dib"&gt;&lt;/p&gt;&lt;section&gt;一切准备就绪后，就可以运行了，项目中介绍了 ChatLLaMA 7B 的训练示例，感兴趣的小伙伴可以查看原项目。&lt;/section&gt;&lt;section&gt;&lt;br&gt;&lt;/section&gt;&lt;section&gt;&lt;em&gt;参考链接：&lt;/em&gt;&lt;/section&gt;&lt;section&gt;&lt;em&gt;https://www.linkedin.com/posts/activity-7035964259431763970-YdMK/&lt;/em&gt;&lt;/section&gt;&lt;section&gt;&lt;em&gt;https://medium.com/@enryu9000/mini-post-first-look-at-llama-4403517d41a1&lt;/em&gt;&lt;/section&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62659-meta-%E5%BC%80%E6%BA%90-chatgpt</guid>
      <pubDate>Sun, 05 Mar 2023 13:17:00 CST</pubDate>
    </item>
    <item>
      <title>玩游戏的儿童在大脑功能测试中得分更高</title>
      <link>https://itindex.net/detail/62467-%E6%B8%B8%E6%88%8F-%E5%84%BF%E7%AB%A5-%E5%A4%A7%E8%84%91</link>
      <description>根据发表在《JAMA Netw Open》期刊上的 &lt;a href="https://jamanetwork.com/journals/jamanetworkopen/fullarticle/2797596?utm_source=For_The_Media&amp;utm_medium=referral&amp;utm_campaign=ftm_links&amp;utm_term=102422"&gt;一项研究&lt;/a&gt;，玩游戏的青少年比不玩的人 &lt;a href="https://www.theverge.com/2022/10/24/23420502/video-game-kid-brain-function-fmri" target="_blank"&gt;有更好的记忆力和更好的运动控制技巧&lt;/a&gt;。研究只是展现了某种相关性，并不能从中得出因果联系，但研究为治疗认知问题的游戏开发提供了依据。该研究利用了来自 &lt;a href="https://abcdstudy.org/"&gt;青少年大脑认知发展（ABCD）&lt;/a&gt;的数据，跟踪了成千上万儿童成长过程中的大脑发育，参与者会定期参加评估，包括大脑成像、认知任务、心理健康筛查、身体健康检查等测试。针对游戏和认知能力的研究使用了来自 2217 名 9 岁和 10 岁儿童的数据。研究人员将他们分成视频游戏组（每周至少玩游戏 21 小时）和非视频游戏组。偶尔玩视频游戏的儿童不包含在研究中。研究人员观测了他们在注意力、冲动控制和记忆测试中的表现。研究发现，玩游戏的儿童表现更出色，而且他们有着不同的大脑活跃模式，大脑记忆力和注意力区域更活跃。他们的心理健康则没有什么差异。&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62467-%E6%B8%B8%E6%88%8F-%E5%84%BF%E7%AB%A5-%E5%A4%A7%E8%84%91</guid>
      <pubDate>Tue, 25 Oct 2022 15:55:08 CST</pubDate>
    </item>
    <item>
      <title>Linux主机性能测试方法</title>
      <link>https://itindex.net/detail/62452-linux-%E4%B8%BB%E6%9C%BA-%E6%80%A7%E8%83%BD</link>
      <description>&lt;h2&gt;背景&lt;/h2&gt; &lt;p&gt;最近打算用躺家吃灰的树莓派4B搭一个NAS，用来快捷方便地访问和备份一些资源。由于备选的硬件（芯片、硬盘、网线、路由器等）和软件（内网穿透技术）的技术选型比较多，这时候就需要有一个能简单评估服务性能的方法。因此简单搜寻了一下常见方案，方便在技术选型时有个统一的对比标准，并且对一些常见指标能在数量级上有一些感性的理解。&lt;/p&gt; &lt;h2&gt;硬盘&lt;/h2&gt; &lt;p&gt;对于硬盘的读写速度测试，首先我们需要注意根据读写的实现细节不同，测试出的结果会有很大的差别。例如对于读来说，是否走缓存读、缓存的大小如何；对于写来说，是否只写缓存、是否同步等待刷盘、刷盘的时机如何，等等。&lt;/p&gt; &lt;p&gt;在实际测试的时候一定要明确自己使用的是哪种IO模式，否则就会得到一些似是而非的结论。&lt;/p&gt; &lt;h3&gt;设备查询&lt;/h3&gt; &lt;p&gt;在测试硬盘前，我们首先得知道我们有哪些硬盘、分别对应哪些分区。用   &lt;code&gt;lsblk&lt;/code&gt; （list block）命令可以查看当前机器下挂载的块设备：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda           8:0    1 29.3G  0 disk
└─sda1        8:1    1 29.3G  0 part /media/pi/5615-BDE2
mmcblk0     179:0    0 59.5G  0 disk
├─mmcblk0p1 179:1    0  256M  0 part /boot
└─mmcblk0p2 179:2    0 59.2G  0 part /&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;可以看到，这里的 sda (SATA device a) 表示我外部插入的一个U盘；mmcblk0 (Multimedia card block 0) 表示树莓派自带的一张 SD 卡。这两个类型是 disk，也就是实体磁盘。&lt;/p&gt; &lt;p&gt;每个 disk 会被分成多个 partition，也就是这里的 sda1 和 mmcblk0p1、mmcblk0p2。每个 partition 又会 mount 到不同的文件夹下，用于在文件系统中进行访问。因此对于文件系统本身来说，我们只会关心到 partition 层面。&lt;/p&gt; &lt;p&gt;通过   &lt;code&gt;df&lt;/code&gt; 命令我们也能直接观察到所有分区的挂载情况：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        59G   11G   46G  19% /
devtmpfs        3.5G     0  3.5G   0% /dev
tmpfs           3.7G     0  3.7G   0% /dev/shm
tmpfs           3.7G   65M  3.6G   2% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           3.7G     0  3.7G   0% /sys/fs/cgroup
/dev/mmcblk0p1  253M   32M  221M  13% /boot
tmpfs           738M  4.0K  738M   1% /run/user/1000
/dev/sda1        30G   23M   30G   1% /media/pi/5615-BDE2&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;同时，disk 和 partition 的详细信息也可以通过   &lt;code&gt;fdisk&lt;/code&gt; 命令查看：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo fdisk -l
Disk /dev/mmcblk0: 59.5 GiB, 63864569856 bytes, 124735488 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x140cee6b

Device         Boot  Start       End   Sectors  Size Id Type
/dev/mmcblk0p1        8192    532479    524288  256M  c W95 FAT32 (LBA)
/dev/mmcblk0p2      532480 124735487 124203008 59.2G 83 Linux


Disk /dev/sda: 29.3 GiB, 31457280000 bytes, 61440000 sectors
Disk model: ProductCode
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xf3203eea

Device     Boot Start      End  Sectors  Size Id Type
/dev/sda1        2048 61439999 61437952 29.3G  7 HPFS/NTFS/exFAT&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;总之，在这里我们只需要搞清楚我们想测试的磁盘和分区分别是哪个即可。&lt;/p&gt; &lt;h3&gt;hdparm&lt;/h3&gt; &lt;p&gt;对于读性能测试，我们一般可以用   &lt;a href="https://sourceforge.net/projects/hdparm/"&gt;hdparm&lt;/a&gt; 工具（hard disk parameter? hardware device parameter?）。Debian下直接 apt 安装即可：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo apt install hdparm -y&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;hdparm目前只支持磁盘读性能测试，提供了三种方式进行测试：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;直接读内存：    &lt;code&gt;sudo hdparm -T [device]&lt;/code&gt; 。&lt;/li&gt;  &lt;li&gt;带buffer读磁盘：    &lt;code&gt;sudo hdparm -t [device]&lt;/code&gt; 。&lt;/li&gt;  &lt;li&gt;不带buffer读磁盘:    &lt;code&gt;sudo hdparm -t --direct [device]&lt;/code&gt; 。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;以我的 mmcblk0 设备为例，跑出来结果分别如下（当然，每次测试建议跑多次取平均值，这里偷个懒）：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo hdparm -T /dev/mmcblk0

/dev/mmcblk0:
 Timing cached reads:   1840 MB in  2.00 seconds = 921.43 MB/sec
 
$ sudo hdparm -t /dev/mmcblk0

/dev/mmcblk0:
 Timing buffered disk reads: 130 MB in  3.04 seconds =  42.74 MB/sec
 
$ sudo hdparm -t --direct /dev/mmcblk0

/dev/mmcblk0:
 Timing O_DIRECT disk reads: 124 MB in  3.01 seconds =  41.17 MB/sec
 &lt;/code&gt;&lt;/pre&gt; &lt;p&gt;显然，走内存读是飞快，不过对于测试磁盘性能来说没有任何意义；不带buffer看起来比带buffer要慢一点点，差别不太大；考虑到现实场景中大多数都是带buffer的读，因此我们在比较时用带buffer读的结果来进行参考即可。&lt;/p&gt; &lt;h3&gt;dd&lt;/h3&gt; &lt;p&gt;dd (data definition? data duplicator?) 是进行磁盘操作、文件生成之类的常用工具。在 gnu 的 coreutils 下，类 Unix 发行版几乎都自带。&lt;/p&gt; &lt;h4&gt;写性能&lt;/h4&gt; &lt;p&gt;一般我们会用 dd 来进行磁盘写性能测试，一般来说也有三种方式：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;直接写内存：    &lt;code&gt;dd bs=1M count=256 if=/dev/zero of=test&lt;/code&gt; 。&lt;/li&gt;  &lt;li&gt;使用内存做缓存写完后一次性刷盘：   &lt;code&gt;dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync&lt;/code&gt;。&lt;/li&gt;  &lt;li&gt;使用内存做缓存，每写完一部分就刷一次盘：   &lt;code&gt;dd bs=1M count=256 if=/dev/zero of=test oflag=dsync&lt;/code&gt;。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;还是以我的 mmcblk0 设备为例（当前目录即挂载的 mmcblk0 设备），跑出来结果分别如下（当然，每次测试建议跑多次取平均值，这里还是偷个懒）：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ dd bs=1M count=256 if=/dev/zero of=test
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 1.39574 s, 192 MB/s

$ dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 31.739 s, 8.5 MB/s

$ dd bs=1M count=256 if=/dev/zero of=test oflag=dsync
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 39.694 s, 6.8 MB/s

$ rm test&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;可见写内存不sync的确还是快，不过还是没啥参考意义。考虑到实际情况下大部分程序都是采用 fdatasync 的模式来写，因此我们在比较时用这个数据即可。&lt;/p&gt; &lt;h4&gt;读性能&lt;/h4&gt; &lt;p&gt;当然，有人也会利用 dd 进行读性能测试，比如：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo dd bs=1M count=256 if=/dev/mmcblk0 of=/dev/null
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 6.03649 s, 44.5 MB/s&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;看起来很美好，结果也和 hdparm 差不多。但是当你第二次再跑这个命令的时候，由于写缓存的存在，结果会快特别多：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo dd bs=1M count=256 if=/dev/mmcblk0 of=/dev/null
256+0 records in
256+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 0.267236 s, 1.0 GB/s&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;显然这样的测试是没有意义的。考虑到这种测试方法甚至很难做到幂等，这里还是不建议用 dd 来测试读性能。&lt;/p&gt; &lt;h3&gt;小结&lt;/h3&gt; &lt;p&gt;对于磁盘读性能测试，建议使用   &lt;code&gt;sudo hdparm -t [device]&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;对于磁盘写性能测试，建议使用   &lt;code&gt;dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync&lt;/code&gt;。&lt;/p&gt; &lt;h2&gt;网络&lt;/h2&gt; &lt;p&gt;网速测试一般分两种，一种是测试当前设备对普通公网设备的读写速度；另一种是点对点测试两个服务器之间的速度。&lt;/p&gt; &lt;h3&gt;speedtest&lt;/h3&gt; &lt;p&gt;speedtest 工具在各地都有测速服务器，通过命令行（speedtest-cli）或者网页（https://speedtest.cn，https://speedtest.net）都可以进行网速上下行的测试。这里以命令行为例：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo apt install speedtest-cli -y

$ speedtest-cli
Retrieving speedtest.net configuration...
Testing from China Mobile (183.192.82.69)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by Chinamobile-5G (Shanghai) [8.49 km]: 6.054 ms
Testing download speed................................................................................
Download: 128.24 Mbit/s
Testing upload speed......................................................................................................
Upload: 22.88 Mbit/s&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;虽然多次测试可能会访问到不同的测试点、导致结果有区别，不过其实也大差不差了。&lt;/p&gt; &lt;p&gt;在使用时偶尔会遇到返403，不要慌，多试几下一般就好了 。&lt;/p&gt; &lt;h3&gt;iperf3&lt;/h3&gt; &lt;p&gt;如果我们并不是想测试公网网速，而是测试两个服务器之间点对点的网速。这时用   &lt;a href="https://iperf.fr/iperf-doc.php#doc"&gt;iperf3&lt;/a&gt; 工具就好。&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo apt install iperf3 -y # Debian 下
$ brew install iperf3        # MacOS 下&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;需要注意，除了 iperf3 之外，还有一个 iperf。这两个版本分别由不同组织开发，前后也不兼容。虽然似乎 iperf3 有坑，不过似乎功能多一点，尤其是支持了下行带宽测试（iperf 只支持上行带宽测试），所以这里还是用 iperf3。&lt;/p&gt; &lt;p&gt;iperf3 是 C/S 架构，服务端开启 server ，客户端开启 client，然后互相通信进行测速。以我在家的树莓派和一个在 HK 的 Azure 主机为例：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ iperf3 -s -p 5555                   # azure 主机上开启 server

$ iperf3 -c 104.208.65.181 -p 5555    # pi 上开启 client 并连接 server&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;测试完成后，client 上会有报告：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ iperf3 -c 104.208.65.181 -p 5555
Connecting to host 104.208.65.181, port 5555
[  5] local 192.168.1.2 port 35994 connected to 104.208.65.181 port 5555
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  8.68 MBytes  72.8 Mbits/sec    0   2.95 MBytes
[  5]   1.00-2.00   sec  1.25 MBytes  10.5 Mbits/sec  2018    252 KBytes
[  5]   2.00-3.00   sec  2.50 MBytes  21.0 Mbits/sec  1076    286 KBytes
[  5]   3.00-4.00   sec  3.75 MBytes  31.5 Mbits/sec  861    237 KBytes
[  5]   4.00-5.00   sec  2.50 MBytes  21.0 Mbits/sec   99    187 KBytes
[  5]   5.00-6.00   sec  1.25 MBytes  10.5 Mbits/sec    0    206 KBytes
[  5]   6.00-7.00   sec  2.50 MBytes  21.0 Mbits/sec    0    215 KBytes
[  5]   7.00-8.00   sec  2.50 MBytes  21.0 Mbits/sec    0    218 KBytes
[  5]   8.00-9.00   sec  2.50 MBytes  21.0 Mbits/sec    0    218 KBytes
[  5]   9.00-10.00  sec  2.50 MBytes  21.0 Mbits/sec    0    220 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  29.9 MBytes  25.1 Mbits/sec  4054             sender
[  5]   0.00-10.09  sec  26.5 MBytes  22.1 Mbits/sec                  receiver

iperf Done.&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这里可以看出 client 对 server 的上行带宽大约是 22Mbit/sec。&lt;/p&gt; &lt;p&gt;类似的，server配置不变，client加上 -R 参数后可以测试出 server 对 client 的下行带宽：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ iperf3 -c 104.208.65.181 -p 5555 -R
Connecting to host 104.208.65.181, port 5555
Reverse mode, remote host 104.208.65.181 is sending
[  5] local 192.168.1.2 port 36000 connected to 104.208.65.181 port 5555
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  6.47 MBytes  54.3 Mbits/sec
[  5]   1.00-2.00   sec  25.7 MBytes   216 Mbits/sec
[  5]   2.00-3.00   sec  13.2 MBytes   111 Mbits/sec
[  5]   3.00-4.00   sec  16.3 MBytes   137 Mbits/sec
[  5]   4.00-5.00   sec  12.4 MBytes   104 Mbits/sec
[  5]   5.00-6.00   sec  14.4 MBytes   121 Mbits/sec
[  5]   6.00-7.00   sec  14.0 MBytes   117 Mbits/sec
[  5]   7.00-8.00   sec  13.9 MBytes   116 Mbits/sec
[  5]   8.00-9.00   sec  11.5 MBytes  96.7 Mbits/sec
[  5]   9.00-10.00  sec  11.3 MBytes  95.2 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.09  sec   142 MBytes   118 Mbits/sec  2560             sender
[  5]   0.00-10.00  sec   139 MBytes   117 Mbits/sec                  receiver

iperf Done.&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这里可以看出 server 对 client 的下行带宽大约是 117Mbit/sec。&lt;/p&gt; &lt;h2&gt;CPU&amp;amp;内存&lt;/h2&gt; &lt;h3&gt;sysbench&lt;/h3&gt; &lt;p&gt;对于CPU和内存的性能测试，可以使用   &lt;a href="https://github.com/akopytov/sysbench"&gt;sysbench&lt;/a&gt; 工具。&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sudo apt install sysbench -y # Debian 下
$ brew install sysbench        # MacOS 下&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;对于我的树莓派测试如下：&lt;/p&gt; &lt;p&gt;CPU单线程测试，可以看出单核每秒操作数大约1483：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sysbench cpu --threads=1 run
sysbench 1.0.18 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Initializing random number generator from current time


Prime numbers limit: 10000

Initializing worker threads...

Threads started!

CPU speed:
    events per second:  1483.17

General statistics:
    total time:                          10.0002s
    total number of events:              14839

Latency (ms):
         min:                                    0.67
         avg:                                    0.67
         max:                                    1.88
         95th percentile:                        0.68
         sum:                                 9992.91

Threads fairness:
    events (avg/stddev):           14839.0000/0.00
    execution time (avg/stddev):   9.9929/0.00
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;内存读写测试，可以看出读写速度约是 1814MiB 每秒：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ sysbench memory run
sysbench 1.0.18 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Initializing random number generator from current time


Running memory speed test with the following options:
  block size: 1KiB
  total size: 102400MiB
  operation: write
  scope: global

Initializing worker threads...

Threads started!

Total operations: 18590983 (1858174.07 per second)

18155.26 MiB transferred (1814.62 MiB/sec)


General statistics:
    total time:                          10.0001s
    total number of events:              18590983

Latency (ms):
         min:                                    0.00
         avg:                                    0.00
         max:                                    0.20
         95th percentile:                        0.00
         sum:                                 4463.57

Threads fairness:
    events (avg/stddev):           18590983.0000/0.00
    execution time (avg/stddev):   4.4636/0.00
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;需要注意的是，对 MacOS 的 CPU benchmark 似乎有坑，测试出来的结果异常的大，不具有参考价值。&lt;/p&gt; &lt;h3&gt;md5sum&lt;/h3&gt; &lt;p&gt;虽然 sysbench 的基准测试看起来比较靠谱，但是实际环境下，真正的执行效率还跟执行的指令啥的都有挺大关系。比如，我们以执行 md5 的速度来对比 CPU 的执行效率：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;$ dd if=/dev/zero bs=1M count=1024 | md5sum
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 5.81996 s, 184 MB/s
cd573cfaace07e7949bc0c46028904ff  -&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;通过   &lt;code&gt;dd&lt;/code&gt; 命令向   &lt;code&gt;md5sum&lt;/code&gt; 持续发送 1GB 的数据进行计算，计算的速度也可以看成是 CPU 单核性能的一种指标。（考虑到管道操作是单线程，这个指令其实也只能用到一个核）&lt;/p&gt; &lt;p&gt;然后，如果我们多找几个 CPU 进行以下对比，我们就会发现一些神奇的现象：&lt;/p&gt; &lt;h4&gt;Cortex-A72（ARM）&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;sysbench 单线程：1479.99 event/s&lt;/li&gt;  &lt;li&gt;dd + md5sum：184 MB/s&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;Neoverse-N1（ARM）&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;sysbench 单线程：3497.70 event/s&lt;/li&gt;  &lt;li&gt;dd + md5sum：424 MB/s&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;Intel(R) Xeon(R) Platinum 8171M CPU @ 2.60GHz（x86_64）&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;sysbench 单线程：820.19 event/s&lt;/li&gt;  &lt;li&gt;dd + md5sum：409 MB/s&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;Intel(R) Xeon(R) CPU E5-26xx v4（x86_64）&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;sysbench 单线程：927.74 event/s&lt;/li&gt;  &lt;li&gt;dd + md5sum：459 MB/s&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz（x86_64）&lt;/h4&gt; &lt;ul&gt;  &lt;li&gt;sysbench 单线程：1054.30 event/s&lt;/li&gt;  &lt;li&gt;dd + md5sum：515 MB/s&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;可以发现 ARM 架构的机器在 sysbench 上表现几乎都比 X86_64 的机器好很多，但是实际跑 md5sum 却相差不大甚至差不少。&lt;/p&gt; &lt;p&gt;可见不同架构间二者的指标并不完全正相关；不过相同架构间二者的指标还是基本正相关的。&lt;/p&gt; &lt;h2&gt;参考资料&lt;/h2&gt; &lt;p&gt;  &lt;a href="https://romanrm.net/dd-benchmark"&gt;dd-benchmark&lt;/a&gt;  &lt;a href="https://man7.org/linux/man-pages/man8/hdparm.8.html"&gt; &lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;a href="https://man7.org/linux/man-pages/man8/hdparm.8.html"&gt;man-hdparm&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;a href="https://iperf.fr/iperf-doc.php#doc"&gt;iperf&lt;/a&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Linux Raspberry Pi</category>
      <guid isPermaLink="true">https://itindex.net/detail/62452-linux-%E4%B8%BB%E6%9C%BA-%E6%80%A7%E8%83%BD</guid>
      <pubDate>Thu, 13 Oct 2022 17:11:12 CST</pubDate>
    </item>
    <item>
      <title>广州上半年智能网联汽车开放测试道路 202 条，有效测试里程较去年底增长 73.5%</title>
      <link>https://itindex.net/detail/62361-%E5%B9%BF%E5%B7%9E-%E6%99%BA%E8%83%BD%E7%BD%91-%E6%B1%BD%E8%BD%A6</link>
      <description>&lt;p&gt;IT之家8 月 13 日消息，广州市交通运输局官网昨日发布公告称，今年上半年，随着参与智能网联汽车道路测试工作的企业主体、测试车辆、测试安全员数量的快速增长，广州市智能网联汽车道路测试步入新阶段。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#22270;&amp;#24418;&amp;#29992;&amp;#25143;&amp;#30028;&amp;#38754;, &amp;#25991;&amp;#26412;, &amp;#24212;&amp;#29992;&amp;#31243;&amp;#24207;, &amp;#30005;&amp;#23376;&amp;#37038;&amp;#20214;" src="https://img.ithome.com/newsuploadfiles/2022/8/ba6f43f6-d921-478b-bd69-818a24f33ddd.png?x-bce-process=image/watermark,image_aW1nL3dhdGVybWFyay9xYy9xYzEyMC5wbmc=,g_3,x_12,y_12,a_0,t_100"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;公告显示，  &lt;strong&gt;广州开放测试道路较 2021 年底增长 57.6%&lt;/strong&gt;，车辆首次发放路测牌照较 2021 年底增长 41.6%，  &lt;strong&gt;有效测试里程较 2021 年底增长 73.5%&lt;/strong&gt;，并且测试车辆交通事故认定记录为零。&lt;/p&gt; &lt;p&gt;具体来说，广州市智能网联汽车道路测试步入新阶段主要体现在 4 个方面。一是测试道路开放范围越来越大。截至 2022 年 6 月 30 日，  &lt;strong&gt;广州市发布开放测试道路 202 条&lt;/strong&gt;，双向里程 789 公里，覆盖白云、海珠、番禺、黄埔、花都、南沙 6 个行政区域。&lt;/p&gt; &lt;p&gt;二是测试规模和类型越来越多。广州 11 家企业的 19 款车型共 201 台测试车辆获得了测试牌照。在今年上半年，广州市新增测试车辆 59 台，升级载客测试 36 台，升级道路测试 46 台，升级远程测试 3 台，自动驾驶里程约 207 万公里。此外，  &lt;strong&gt;广州自动驾驶道路测试总里程已超过 600 万公里。&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;三是行业监管越来越规范。广州市率先搭建了“智能网联汽车道路测试实时监管平台”，实现了车辆监控、视频监控、电子围栏、基础数据及测试道路管理、风险预警及事故溯源等功能。&lt;/p&gt; &lt;p&gt;四是示范应用（运营）越来越扎实。例如，广州市交通运输局依据自动驾驶企业小马慧行近期的道路测试表现，将其 3 款车型首批纳入广州市智能网联汽车（自动驾驶）示范运营车型目录。这意味着广州市在车辆准入、上牌、运营许可等方面的进一步规范，标志着广州市在在全国率先实现了智能网联汽车商业化运营。&lt;/p&gt; &lt;p&gt;IT之家了解到，工业和信息化部装备工业一司副司长郭守刚于 3 月 25 日表示，  &lt;a href="https://www.ithome.com/0/609/659.htm" target="_blank"&gt;工信部正在加快推进智能网联汽车道路测试示范&lt;/a&gt;。截至 3 月 25 日，全国已开放测试道路里程超过 5000 公里，安全测试里程超过一千万公里，带动智能化道路改造升级超过 3500 公里。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62361-%E5%B9%BF%E5%B7%9E-%E6%99%BA%E8%83%BD%E7%BD%91-%E6%B1%BD%E8%BD%A6</guid>
      <pubDate>Sat, 13 Aug 2022 08:58:40 CST</pubDate>
    </item>
    <item>
      <title>收钱吧高效自动化测试实践</title>
      <link>https://itindex.net/detail/62276-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%B5%8B%E8%AF%95-%E5%AE%9E%E8%B7%B5</link>
      <description>&lt;p&gt;编辑 | 邵一帆&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;h2&gt;1. 背景&lt;/h2&gt; &lt;p&gt;收钱吧业务服务千万级商家，业务庞大，产品背后有复杂的应用支撑。我们采用了微服务架构，有成百上千个不同类型的后端服务，使用了包括Node.js、Java、Go、Python等后端语言，还有Mysql、MongoDB等数据库以及Elasticsearch、Kafka、Redis、Apollo、RabbitMQ等中间件。&lt;/p&gt; &lt;p&gt;随着产品需求复杂性的不断上升，传统功能测试的片面性及滞后性导致测试成本急剧增加、测试效率大幅度下降，仅靠功能测试已难以持续保障项目质量及交付效率。&lt;/p&gt; &lt;p&gt;而自动化测试可以帮忙测试人员在项目初期就能发现系统深层次的问题，并且降低了问题修复的时间成本，其好处显而易见。自动化测试也在各大互联网公司逐步地落地，这是大势所趋，收钱吧质量工程部在这几年里一直在践行高效的自动化测试，并且有了一些成果。&lt;/p&gt; &lt;h2&gt;2. 测试策略&lt;/h2&gt; &lt;p&gt;  &lt;strong&gt;自动化测试&lt;/strong&gt;是一个泛称，它包含了诸如单元测试、接口测试、Web 测试等具体测试手段，每个自动化测试手段有其优劣势，找到适合收钱吧技术状况的自动化策略是能够实践成功的第一步。&lt;/p&gt; &lt;p&gt;在微服务架构下，相比测试金字塔  &lt;sup&gt;[1]&lt;/sup&gt;，我们更加推崇蜂窝型分层  &lt;sup&gt;[2]&lt;/sup&gt;。&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;这是因为：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;对于微服务模型的项目来说，服务即『单元』，接口表达了『单元』的能力，并实现了单元之间的通讯，编排接口的调用则完成了业务、产品逻辑；&lt;/li&gt;  &lt;li&gt;单元测试代码量大、开发工程师无法投入较多的精力进行维护，且不能够通过单元测试来实现服务之间的集成测试、覆盖业务场景，实际收益其实并不明显；&lt;/li&gt;  &lt;li&gt;在项目初期有接口定义的情况下，测试工程师可以较早地介入项目当中设计、开发接口测试用例，无需等开发工作全部完成，实现测试左移，可以更早地发现系统问题，提高了整体的效率。&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;接口自动化测试兼顾了介入早、维护成本低、业务逻辑覆盖完整等优点，因此它成了我们自动化测试重点投入的方向。&lt;/p&gt; &lt;h3&gt;2.1 细化微服务下的测试策略&lt;/h3&gt; &lt;p&gt;除了明确  &lt;strong&gt;接口测试是重点&lt;/strong&gt;以外，我们还要基于微服务架构的分层特点，进一步细化自动化测试策略。&lt;/p&gt; &lt;p&gt;我们简明扼要的描述下当前的系统架构，如图：&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;ul&gt;  &lt;li&gt;   &lt;strong&gt;接入层&lt;/strong&gt;：面向客户端，客户端通过调用接入层的接口来请求应用层的服务，得到响应后，包装数据返回，给客户端使用。比如API网关，它的主要职责包含请求认证鉴权、安全校验、返回值的包装、请求的转发，其本身并没有业务逻辑；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;应用层&lt;/strong&gt;：服务面向业务，它通过对一个或者多个领域层服务的调用、编排来实现业务功能。例如我们系统中的业务开通服务，它依赖用户中心、商户中心、进件、银行卡等领域层的服务来实现其功能；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;领域层&lt;/strong&gt;：面向领域对象，领域对象是具有高内聚、低耦合的特点，且具有单一的职责。例如我们系统中的支付服务、银行卡服务、分账服务等，它们只实现本身的业务规则和逻辑，可能会依赖一些三方服务、内部服务；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;基础设施层&lt;/strong&gt;：如关系型数据库、缓存服务、消息队列等。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;成百上千个后端服务有不同的分层界定，我们可以从接入方调用的视角简化成下图所示：&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;基于以上分析，我们了解了服务调用链路以及每层服务的主要职责。然后可以根据每层服务的侧重点，进行策略细分：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;接入层：重点关注接口鉴权、安全校验、输入输出合法性、接口联通性、路由转发的正确性；&lt;/li&gt;  &lt;li&gt;应用层：重点需要根据接口的业务逻辑做到单接口的功能覆盖，且从业务场景考虑，串联组装该层接口去覆盖业务流程，从而达成集成测试的目的；&lt;/li&gt;  &lt;li&gt;领域层：重点关注领域对象的业务规则、算法、与三方交互的逻辑，从而做到接口功能覆盖。&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;3. 自动化测试实践&lt;/h2&gt; &lt;p&gt;在测试策略清晰明确的前提下，我们就可以着手进行自动化用例的开发工作。&lt;/p&gt; &lt;h3&gt;3.1 技术选型&lt;/h3&gt; &lt;p&gt;与大部分公司类似，我们采用了Python作为开发语言，使用内置的unittest  &lt;sup&gt;[3]&lt;/sup&gt;单元测试框架组织测试用例，采取pytest  &lt;sup&gt;[4]&lt;/sup&gt;执行测试用例。&lt;/p&gt; &lt;p&gt;我们测试框架具备的能力大致如下：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;测试数据对象化管理、关联关系管理&lt;/li&gt;  &lt;li&gt;支持数据驱动&lt;/li&gt;  &lt;li&gt;多维度分拣测试用例能力，可以方便组织不同的测试计划&lt;/li&gt;  &lt;li&gt;多环境执行能力&lt;/li&gt;  &lt;li&gt;中间件连接的池化&lt;/li&gt;  &lt;li&gt;自动报告集成&lt;/li&gt;  &lt;li&gt;平台集成&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;3.2 接口功能测试&lt;/h3&gt; &lt;p&gt;本章提到的接口功能测试是针对某一服务端的单个接口的功能测试。在测试过程中，我们需要关注以下几点：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;参数校验：参数的必选校验、参数组合校验、参数类型是否合法校验、取值的边界校验；&lt;/li&gt;  &lt;li&gt;接口权限校验：校验接口的使用权限是否存在水平、垂直越权；&lt;/li&gt;  &lt;li&gt;返回值校验：校验返回值是否符合预期，包括返回格式、响应码、消息体、错误文案、返回数据是否正确；&lt;/li&gt;  &lt;li&gt;持久化校验：落地数据的正确性校验；&lt;/li&gt;  &lt;li&gt;中间件校验：缓存、消息存储逻辑校验；&lt;/li&gt;  &lt;li&gt;接口逻辑验证：接口内部业务逻辑尽量做到分支覆盖。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;与此同时，要想达到高效且高覆盖的测试目标，测试人员需要从用户角度出发并结合开发代码的实现，抽取出有效的等价类进行接口功能测试，如此可以减少大量时间成本，不做过度或者无效的测试。下面举一个例子来说明：&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;如何高效地测试一个登陆接口（用户名、密码）&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;与大部分人设计的用例不同，针对登陆功能，我们只设计2条用例覆盖，而不考虑用户名、密码长了短了、是否包含无效字符等各种错误条件的排列组合：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;用例1：使用正确的用户名，密码登陆&lt;/li&gt;  &lt;li&gt;用例2：使用错误的用户名或密码登陆&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;之所以这样设计，是因为我们提前了解到接口的实现：只是去查询数据库中否存在匹配的用户名和密码，即能够匹配成功的只有一组字符串，而用所有非法的字符串集合去数据库查询都会返回失败，这一类输入都属于异常等价类。因此我们无需去穷举入参进行测试。&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;def test_login_succ(self):   &lt;br /&gt;    res = self.client.login(username, password)   &lt;br /&gt;    self.assertEqual(res.code, SUCC)   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;3.3 接口集成测试&lt;/h3&gt; &lt;p&gt;在单接口功能测试保障的前提下，我们需要保障接口之间交互以及数据流转的正确性。使用多服务的多接口之间的调用，就可以串联业务场景覆盖，这是功能测试覆盖最重要的手段。以下是集成测试用例设计关键点列举：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;strong&gt;业务正常流程覆盖&lt;/strong&gt;：通过各服务的接口调用来组装正常流程上的用例，保证业务流程上各分支的正常流程都被覆盖到；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;业务异常流程覆盖&lt;/strong&gt;：通过调用接口来覆盖业务流程上的异常分支，模拟业务节点返回异常，来测试系统的容错性，以及出错后的后续的业务补偿流程；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;调用链路覆盖&lt;/strong&gt;：保证系统中每条链路都覆盖到，从下游接口发起，覆盖整条链路；&lt;/li&gt;  &lt;li&gt;   &lt;strong&gt;接口时序覆盖&lt;/strong&gt;：接口调用按照时序有效路径去设计，避免无效的路径。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;举例说明，通过接口集成测试来进行测试：&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;新增商户报名支付源活动，费率优惠支付&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;测试设计：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;正确的用例包含新增商户入网、活动报名、报名审批、触发支付源进件、费率生效、优惠支付成功等场景；&lt;/li&gt;  &lt;li&gt;异常用例存在很多种情况，任一流程失败，都会导致最终业务失败；&lt;/li&gt;&lt;/ul&gt; &lt;pre&gt;  &lt;code&gt;def test_new_merchant_pay_success(self):   &lt;br /&gt;    res = self.merchant_service.create_merchant()   &lt;br /&gt;    self.assertEqual(res.code, SUCC)   &lt;br /&gt;   &lt;br /&gt;    another_res = self.another_service.do_something(res.field)   &lt;br /&gt;    self.assertTrue(another_res, COMPLETE)   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;3.4 资损测试&lt;/h3&gt; &lt;p&gt;收钱吧的业务牵涉到资金流动，如支付、分账、分润、代付等等。如果程序控制不当，就会造成严重的资金损失。引起资损的原因有很多，例如用户重复提交、程序并发问题、业务逻辑过程处理不当、金额换算处理不正确、安全漏洞等等。根据不同的业务场景，开发会选择不同的实现方案来解决问题。如唯一索引约束、分布式锁、数据库排他锁等。即便如此，测试人员仍需要进行资损相关的测试，从而保障产品质量。下面列举了三种场景，详细说明如何进行资损测试。&lt;/p&gt; &lt;h4&gt;3.4.1 重复调用&lt;/h4&gt; &lt;blockquote&gt;  &lt;p&gt;当调用方重复发起同一笔请求，系统需要做幂等处理，确保只能扣款一次&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;要验证这个功能，就需要借助自动化测试的能力：模拟同一个用户顺序发起多次同样支付请求、期望只成功一次。&lt;/p&gt; &lt;p&gt;代码样例如下：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;def test_pay_more_time():   &lt;br /&gt;    old_balance = self.client.get_merchant_balance(merchant_id)  # 获取商户余额   &lt;br /&gt;    [self.client.pay(amount, client_sn) for _ in range(5)]  # 多次发起支付   &lt;br /&gt;    current_balance = self.client.get_merchant_balance(merchant_id)  # 重新获取余额   &lt;br /&gt;    assert current_balance == old_balance + amount  # 断言商户余额只增加了1分钱   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;3.4.2 并发请求同一接口&lt;/h4&gt; &lt;blockquote&gt;  &lt;p&gt;当调用方并发多次同一笔支付请求，系统要确保只能扣款一次&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;与上一个场景的区别在于：调用请求不再友善地挨个发起，而是一哄而上，这个时候就需要用多线程模拟同用户并发调用。&lt;/p&gt; &lt;p&gt;代码样例如下：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;def test_pay_concurrency(self):   &lt;br /&gt;    old_balance = self.client.get_merchant_balance(merchant_id)  # 获取商户余额   &lt;br /&gt;   &lt;br /&gt;    pool = [threading.Thread(target=self.client.pay, args=(1,)) for _ in range(10)]   &lt;br /&gt;    [t.start() for t in pool]   &lt;br /&gt;    [t.join() for t in pool]  # 使用多线程并发调用支付请求，金额为1分钱，并发n次   &lt;br /&gt;   &lt;br /&gt;    current_balance = self.client.get_merchant_balance(merchant_id)   &lt;br /&gt;    assert current_balance == old_balance + 1  # 断言商户余额只增加了1分钱   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h4&gt;3.4.3 多种资金流动接口并发调用&lt;/h4&gt; &lt;blockquote&gt;  &lt;p&gt;对同一账户同时发起多次储值服务、核销、退款、多次请求可以成功多次&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;测试用例设计：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;对同一个账户不同的操作进行并发调用；&lt;/li&gt;  &lt;li&gt;并发后结果校验需满足：账户余额变化 = 累计充值金额 - 累计次核销总金额 - 累计退款金额；&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;代码样例如下：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;def test_account()   &lt;br /&gt;    old_balance = self.client.get_merchant_balance(merchant_id)  # 获取商户余额   &lt;br /&gt;   &lt;br /&gt;    concurrent_add(self.client, add_amount, a)  # 并发储值   &lt;br /&gt;    concurrent_reduce(self.client, reduce_amount, b)  # 并发核销   &lt;br /&gt;    concurrent_refund(self.client, refund_amount, c)  # 并发退款   &lt;br /&gt;   &lt;br /&gt;    current_balance = self.client.get_merchant_balance(merchant_id)   &lt;br /&gt;    assert current_balance == old_balance + a*add_amount-b*reduce_amount-c*refund_amount   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;3.5 Mock第三方接口测试&lt;/h3&gt; &lt;p&gt;在我们系统中，很多业务依赖三方接口，而我们无法依赖对方的测试环境来验证我们自身的逻辑，尤其是对依赖接口异常的处理。我们自研了Mock Server，这类场景的测试因此就不依赖第三方的环境。通过Mock Server控制响应异常，可以实现各种异常场景测试。比如：要模拟进件失败，支付时银行卡冻结等。&lt;/p&gt; &lt;p&gt;Mock Server采用高性能响应的go语言框架，而且可以动态调整返回的结果，可以很方便的集成到自动化测试中。如：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@mock(&amp;quot;RETURN_CODE&amp;quot;, &amp;quot;ACCOUNT_ERROR&amp;quot;)   &lt;br /&gt;def test_pay_abnormal():   &lt;br /&gt;    response = self.client.pay()   &lt;br /&gt;    self.assertEqual(response.code, FAIL)   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;4. 平台化管理&lt;/h2&gt; &lt;h3&gt;4.1 自动化执行平台&lt;/h3&gt; &lt;p&gt;我们自研了Zepar——通用的自动化测试执行平台，来解决测试用例呈现、测试计划管理、自动/手动执行的需求。通过平台化管理，大大提高了自动化用例的使用率，而且也方便其他部门的同事应用我们的自动化测试能力。&lt;/p&gt; &lt;img&gt;&lt;/img&gt;测试计划管理 &lt;img&gt;&lt;/img&gt;执行记录 &lt;h3&gt;4.2 报告平台&lt;/h3&gt; &lt;p&gt;另外我们引入了开源的测试报告平台：ReportPortal  &lt;sup&gt;[5]&lt;/sup&gt;。它是一个自动化测试用例日志收集、结果分析的可视化平台，可以与主流测试框架集成，如TestNG、Pytest、Junit、Nunit、SoapUI等。与此同时，该平台可以多维度统计报表，支持在线分析测试结果，并可以展示出美观的分析报告，如下图所示。&lt;/p&gt; &lt;p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;利用ReportPortal，我们强化了失败用例的分析，也能够帮助发现不稳定用例  &lt;sup&gt;[6]&lt;/sup&gt;，及时修复，防止测试用例的腐化。&lt;/p&gt; &lt;h2&gt;5. 防腐化措施&lt;/h2&gt; &lt;p&gt;代码在日常的迭代中，往往会呈现出自然地腐化，从刚开始清晰明了的架构慢慢演变成四不像，越来越难维护，自动化测试用例同样避免不了陷入这种尴尬的境地。&lt;/p&gt; &lt;p&gt;在代码层面，我们要求遵循PEP8  &lt;sup&gt;[7]&lt;/sup&gt;的代码风格，用例设计上要遵循AIR  &lt;sup&gt;[8]&lt;/sup&gt;原则，在Function的docstring  &lt;sup&gt;[9]&lt;/sup&gt;中写清楚测试用例的每一个步骤，并且每次代码提交都需要进行Code Review。在框架中，我们大量利用装饰器  &lt;sup&gt;[10]&lt;/sup&gt;来隐藏一些技术细节，让测试工程师尽可能只关注业务逻辑，这样可以更容易的编写自动化用例。我们鼓励写出Pythonic  &lt;sup&gt;[11]&lt;/sup&gt;的代码，对低质量的代码坚决Say No。&lt;/p&gt; &lt;p&gt;我们没有  &lt;strong&gt;专职&lt;/strong&gt;的自动化测试工程师，也就是说在收钱吧的测试工程师，你既要做业务测试又要维护自动化用例：）。职责清晰，责任界定也就明确，每一个测试工程师都要对他负责业务对应的自动化用例可用性负责，没有什么可推诿的。&lt;/p&gt; &lt;h2&gt;6. 总结与展望&lt;/h2&gt; &lt;p&gt;以上是在收钱吧项目测试中积累的有关自动化测试的一些实践与成果。目前，我们的接口测试用例  &lt;strong&gt;总数达到30000以上&lt;/strong&gt;，可用率**超过95%  &lt;strong&gt;，核心服务代码&lt;/strong&gt;覆盖率更是超过70%**。我们还对测试效率提出了更高的要求：随着自动化用例数量的不断增加，技术实现中出现的大量异步场景，自动化执行耗时正在增长。针对这些痛点，我们正进一步研发分布式执行框架，一劳永逸地解决这些问题。&lt;/p&gt; &lt;p&gt;在当下的敏捷时代，行业要求速度和质量。自动化测试的兴起无外乎成为了行业中的宝贵资源。当严谨的测试方案、完善的测试用例，遇上了高效的自动化测试手段，便成为了提升测试效率和产品质量的一把利器，不仅保证高效的回归测试，缩减大量手工测试，同时将接口用例输出给开发自测回归，给开发增添上线信心。我们将在自动化测试这条道路上不断探索、保持热忱、持续优化，为我们产品的质量保驾护航。&lt;/p&gt; &lt;h2&gt;7. 关于作者&lt;/h2&gt; &lt;p&gt;任斌，来自质量工程部&lt;/p&gt; &lt;h3&gt;参考资料&lt;/h3&gt;[1] &lt;p&gt;Test Pyramid:  &lt;em&gt;https://martinfowler.com/bliki/TestPyramid.html&lt;/em&gt;&lt;/p&gt;[2] &lt;p&gt;Testing of Microservices:  &lt;em&gt;https://engineering.atspotify.com/2018/01/testing-of-microservices/&lt;/em&gt;&lt;/p&gt;[3] &lt;p&gt;Unit testing framework:  &lt;em&gt;https://docs.python.org/3/library/unittest.html&lt;/em&gt;&lt;/p&gt;[4] &lt;p&gt;pytest: helps you write better programs:  &lt;em&gt;https://pytest.io&lt;/em&gt;&lt;/p&gt;[5] &lt;p&gt;Reporting automated testing analytics machine learning :  &lt;em&gt;https://reportportal.io&lt;/em&gt;&lt;/p&gt;[6] &lt;p&gt;Test Flakiness – Methods for identifying and dealing with flaky tests:  &lt;em&gt;https://engineering.atspotify.com/2019/11/test-flakiness-methods-for-identifying-and-dealing-with-flaky-tests/&lt;/em&gt;&lt;/p&gt;[7] &lt;p&gt;PEP 8 – Style Guide for Python Code:  &lt;em&gt;https://peps.python.org/pep-0008/&lt;/em&gt;&lt;/p&gt;[8] &lt;p&gt;单元测试 AIR 原则:  &lt;em&gt;https://juejin.cn/post/6844903923447250957&lt;/em&gt;&lt;/p&gt;[9] &lt;p&gt;PEP 257 – Docstring Conventions
:  &lt;em&gt;https://peps.python.org/pep-0257/&lt;/em&gt;&lt;/p&gt;[10] &lt;p&gt;PEP 318 – Decorators for Functions and Methods
:  &lt;em&gt;https://peps.python.org/pep-0318/&lt;/em&gt;&lt;/p&gt;[11] &lt;p&gt;What is Pythonic?:  &lt;em&gt;https://www.computerhope.com/jargon/p/pythonic.htm#:~:text=Pythonic%20is%20an%20adjective%20that,way%20is%20called%20%22pythonic.%22&lt;/em&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/62276-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%B5%8B%E8%AF%95-%E5%AE%9E%E8%B7%B5</guid>
      <pubDate>Fri, 27 May 2022 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>Metasploit Framework 6.1.32+20220303 (macOS, Linux, Windows) -- 渗透测试框架</title>
      <link>https://itindex.net/detail/62141-metasploit-framework-macos</link>
      <description>&lt;p&gt;请访问原文链接：  &lt;a href="https://sysin.org/blog/metasploit-framework-6/" title="Metasploit Framework 6.1.32+20220303 (macOS, Linux, Windows) -- &amp;#28183;&amp;#36879;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;"&gt;Metasploit Framework 6.1.32+20220303 (macOS, Linux, Windows) -- 渗透测试框架&lt;/a&gt;，查看最新版。原创作品，转载请保留出处。&lt;/p&gt; &lt;p&gt;作者主页：  &lt;a href="https://sysin.org"&gt;www.sysin.org&lt;/a&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="img" src="https://www.metasploit.com/includes/images/metasploit-logo.svg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://sysin.org/#&amp;#19990;&amp;#30028;&amp;#19978;&amp;#26368;&amp;#24191;&amp;#27867;&amp;#20351;&amp;#29992;&amp;#30340;&amp;#28183;&amp;#36879;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;" title="&amp;#19990;&amp;#30028;&amp;#19978;&amp;#26368;&amp;#24191;&amp;#27867;&amp;#20351;&amp;#29992;&amp;#30340;&amp;#28183;&amp;#36879;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;"&gt;&lt;/a&gt;世界上最广泛使用的渗透测试框架&lt;/h2&gt; &lt;p&gt;知识就是力量，尤其是当它被分享时。作为开源社区和 Rapid7 之间的合作，Metasploit 帮助安全团队做的不仅仅是验证漏洞、管理安全评估和提高安全意识；它使防守队员能够始终领先比赛一步（或两步）。&lt;/p&gt; &lt;p&gt;  &lt;img alt="dashboard" src="https://res.cloudinary.com/spiralyze/image/upload/f_auto/RAPID7/1401:%20Metasploit%20Trial%20-%20Redesign/Dashboard_1440.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://sysin.org/#&amp;#29256;&amp;#26412;&amp;#27604;&amp;#36739;" title="&amp;#29256;&amp;#26412;&amp;#27604;&amp;#36739;"&gt;&lt;/a&gt;版本比较&lt;/h2&gt; &lt;h3&gt;  &lt;a href="https://sysin.org/#Open-Source-Metasploit-Framework" title="Open Source: Metasploit Framework"&gt;&lt;/a&gt;Open Source: Metasploit Framework&lt;/h3&gt; &lt;p&gt;  &lt;a href="https://github.com/rapid7/metasploit-framework/wiki/Nightly-Installers" rel="noopener" target="_blank"&gt;Download&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;  &lt;a href="https://sysin.org/#Commercial-Support-Metasploit-Pro" title="Commercial Support: Metasploit Pro"&gt;&lt;/a&gt;Commercial Support: Metasploit Pro&lt;/h3&gt; &lt;p&gt;  &lt;img alt="metasploit-editions-compare" src="https://sysin.org/metasploit-editions-compare.webp"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://sysin.org/#&amp;#19979;&amp;#36733;&amp;#22320;&amp;#22336;" title="&amp;#19979;&amp;#36733;&amp;#22320;&amp;#22336;"&gt;&lt;/a&gt;下载地址&lt;/h2&gt; &lt;p&gt;macOS：metasploit-framework-VERSION.x86_64.pkg  &lt;br /&gt;Windows：metasploit-framework-VERSION-x64.msi  &lt;br /&gt;Linux deb x64：metasploit-framework_VERSION_amd64.deb  &lt;br /&gt;Linux deb x86：metasploit-framework_VERSION_i386.deb  &lt;br /&gt;Linux deb arm64：metasploit-framework_VERSION_arm64.deb  &lt;br /&gt;Linux rpm x64：metasploit-framework-VERSION.el6.x86_64.rpm&lt;/p&gt; &lt;p&gt;百度网盘链接：  &lt;a href="https://pan.baidu.com/s/14jnv2S4EhzdHr3cWnwfNJA" rel="noopener" target="_blank"&gt;https://pan.baidu.com/s/14jnv2S4EhzdHr3cWnwfNJA&lt;/a&gt;  提取码：17p9&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Download macOS Linux Windows Security</category>
      <guid isPermaLink="true">https://itindex.net/detail/62141-metasploit-framework-macos</guid>
      <pubDate>Fri, 04 Mar 2022 15:03:00 CST</pubDate>
    </item>
    <item>
      <title>从 TikTok“重 QA 轻测试”来看中美软件开发之间的差异</title>
      <link>https://itindex.net/detail/62136-tiktok-qa-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;div&gt;[cp]看到InfoQ推送的一篇文章《从 TikTok“重 QA 轻测试”来看中美软件开发之间的差异》http://t.cn/A6671nbJ  ，讲了一位曾在一家在美中企（TikTok）工作了一年多的华裔（之前任职于 Snapchat 和 Facebook），在 YouTube 上发布了一个视频“5 crazy things about working for Tiktok(why we quit our PM and engineering jobs)”，从五个方面总结了他从中国企业里学到的经验。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;感觉整个一个高级黑啊，看起来像夸实际上是在吐槽！完全就是靠堆人力成本来弥补软件工程上的不足。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;原始视频链接：http://t.cn/A6671nbi&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;摘录文中翻译如下：&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;第一点：很多西方企业都会写单元测试，每个人都知道这是非常基本的事情。但这里的中国工程师们不需要编写单元测试！每项代码提交都指望 QA 部门的手动测试，团队在提交之前手动测试每个 code commit 提交。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;你可能认为这完全是疯了，为什么不写单元测试？利用 QA 进行测试，实际上是希望工程师们关注于功能，并快速启动，写测试就完全交给了 QA。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;而且让人震惊的另一件事情是，代码合并请求也不需要批准。在一个十万人的企业里（这不是一个小型初创企业），没有单元测试也没有代码审查，仅依赖于 QA，但这却是“有效”的方式！也没有发生过重大宕机事件。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;中国企业的产品团队往往人员更多，也更倾向于依靠运营团队推动业务增长；这一点与美国被动加数据驱动的增长思路不太一样。我注意到中美科技企业之间的主要差异，是中国企业对人力的依赖性更高，这个优势也是中国企业得以迅速占领新市场的核心原因。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;第二点：在中国企业，很少见到一对一式的会议，因为扩展性太差了。各个团队内部很少进一步细分，所以需要跟更多同事开展交互。经常见到那种典型的、自上而下的会议，一开就是 90 多分钟，期间 60 多人同时参会。大多数情况下，都是 1 个人在前面讲、剩下的人在翻看会议资料。很少有欧美公司里常见的那种畅谈会或者讨论会。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;第三点：为了防止猎头挖人，这里并不公布明确的组织结构体系。组织结构高度扁平，某些工程经理需要接手 200 多份绩效评估报告（未经划分！），有些报告提交者甚至不知道自己的顶头上司长什么样子。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;第四点：从流程与执行上来说，中国企业里的屁事不多，大家都在低头忙工作，很少会去传闲话或者搞道德评判。另一方面，中国企业在流程设计上还不够成熟。文档与改进团队的同事们无论做得多好，但很难得到激励。也没人审查工程师们的代码。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;第五点：从工作与生活的平衡上来说，美国团队不需要 996，但要求必须适应中国时区。这真挺难的，也是造成人员流失的主要原因，我接触过的所有 PM 都在工作一年后离职了。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;中国的 STEM（科学、技术、工程与数学）专业博士是美国的四倍，但技术岗位反而比美国更少，所以这里的竞争烈度要高于美国，大家把这种状况称为“内卷”。中国的同事们很怕自己失去技术优势并被社会的发展甩在身后，所以他们才能迸发出巨大的工作能量。 http://t.cn/A6671D2n[/cp]&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62136-tiktok-qa-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Wed, 02 Mar 2022 13:18:41 CST</pubDate>
    </item>
    <item>
      <title>Jenkins 集成 JMeter 实现持续性能测试</title>
      <link>https://itindex.net/detail/62077-jenkins-jmeter-%E6%80%A7%E8%83%BD</link>
      <description>&lt;p&gt;JMeter 是最受欢迎的开源性能测试工具之一，最近在跟多个用户的聊天中都谈到了JMeter与CI/CD工具集成，我们这个话题来聊一下，如何使用Jenkins和Performance plugin实现持续性能测试。&lt;/p&gt; &lt;a&gt;&lt;/a&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#&amp;#21069;&amp;#25552;&amp;#26465;&amp;#20214;" title="&amp;#21069;&amp;#25552;&amp;#26465;&amp;#20214;"&gt;&lt;/a&gt;前提条件&lt;/h2&gt; &lt;p&gt;我们在使用Jenkins集成JMeter之前，需要现有一个正常运行状态的Jenkins实例。如果还没有Jenkins实例，您可以使用Jenkins的Docker镜像快速安装一个。安装命令如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;docker run -d -p 80:8080 -p 50000:50000 -v /root/jenkins_home:/var/jenkins_home -v /etc/localtime:/etc/localtime --name jenkins     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;img alt="Jenkins &amp;#30331;&amp;#24405;" src="https://devopstools.cn/images/Jenkins-Login.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#&amp;#19979;&amp;#36733;JMeter-&amp;#21040;Jenkins&amp;#30340;&amp;#23487;&amp;#20027;&amp;#26426;" title="&amp;#19979;&amp;#36733;JMeter &amp;#21040;Jenkins&amp;#30340;&amp;#23487;&amp;#20027;&amp;#26426;"&gt;&lt;/a&gt;下载JMeter 到Jenkins的宿主机&lt;/h2&gt; &lt;p&gt;到Apache官网下载JMeter，下载地址如下：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.4.3.tgz     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;由于我们要部署在Linux平台，因此选择下载.tgz后缀的压缩包  &lt;br /&gt;  &lt;img alt="JMeter&amp;#19979;&amp;#36733;" src="https://devopstools.cn/images/JMeter-Download.png"&gt;&lt;/img&gt;  &lt;br /&gt;JMeter压缩包下载到宿主机的 `/root/jenkins_home/tools’目录下，并使用 · tar -zxvf ·进行解压&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;tar -zxvf apache-jmeter-5.4.3.tgz     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;解压后，验证JMeter在Jenkins Docker容器中是否可以正常启动.&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;查看Jenkins Docker容器名称或ID   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;2       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;[root@jenkins-master tools]# docker ps |grep jenkins       &lt;br /&gt;de0d56bd751e   jenkins/jenkins:lts                                       &amp;quot;/sbin/tini -- /usr/…&amp;quot;   3 months ago   Up 2 hours              0.0.0.0:50000-&amp;gt;50000/tcp, :::50000-&amp;gt;50000/tcp, 0.0.0.0:8085-&amp;gt;8080/tcp, :::8085-&amp;gt;8080/tcp         &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/li&gt;  &lt;li&gt;以Root用户方式进入Jenkins容器   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;[root@jenkins-master tools]# docker exec -it -u root jenkins bash       &lt;br /&gt;root@de0d56bd751e:/#        &lt;br /&gt;       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/li&gt;  &lt;li&gt;切换到jenkins的tools目录下   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;4       &lt;br /&gt;5       &lt;br /&gt;6       &lt;br /&gt;7       &lt;br /&gt;8       &lt;br /&gt;9       &lt;br /&gt;10       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;root@de0d56bd751e:/var/jenkins_home/tools/apache-jmeter-5.4.3/bin# cd /var/jenkins_home/tools/apache-jmeter-5.4.3/bin       &lt;br /&gt;root@de0d56bd751e:/var/jenkins_home/tools/apache-jmeter-5.4.3/bin# ls       &lt;br /&gt;ApacheJMeter.jar          examples        jmeter-n.cmd       jmeter.sh          report-template             system.properties       &lt;br /&gt;BeanShellAssertion.bshrc  hc.parameters   jmeter-server      jmeterw.cmd        reportgenerator.properties  templates       &lt;br /&gt;BeanShellFunction.bshrc   heapdump.cmd    jmeter-server.bat  krb5.conf          saveservice.properties      threaddump.cmd       &lt;br /&gt;BeanShellListeners.bshrc  heapdump.sh     jmeter-t.cmd       log4j2.xml         shutdown.cmd                threaddump.sh       &lt;br /&gt;BeanShellSampler.bshrc    jaas.conf       jmeter.bat         mirror-server      shutdown.sh                 upgrade.properties       &lt;br /&gt;create-rmi-keystore.bat   jmeter          jmeter.log         mirror-server.cmd  stoptest.cmd                user.properties       &lt;br /&gt;create-rmi-keystore.sh    jmeter-n-r.cmd  jmeter.properties  mirror-server.sh   stoptest.sh                 utility.groovy       &lt;br /&gt;       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/li&gt;  &lt;li&gt;检查JMeter在Jenkins容器中是否可以正常运行   &lt;table&gt;    &lt;tr&gt;     &lt;td&gt;      &lt;pre&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;4       &lt;br /&gt;5       &lt;br /&gt;6       &lt;br /&gt;7       &lt;br /&gt;8       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;     &lt;td&gt;      &lt;pre&gt;root@de0d56bd751e:/var/jenkins_home/tools/apache-jmeter-5.4.3/bin# /var/jenkins_home/tools/apache-jmeter-5.4.3/bin/jmeter --version       &lt;br /&gt;    _    ____   _    ____ _   _ _____       _ __  __ _____ _____ _____ ____       &lt;br /&gt;   / \  |  _ \ / \  / ___| | | | ____|     | |  \/  | ____|_   _| ____|  _ \       &lt;br /&gt;  / _ \ | |_) / _ \| |   | |_| |  _|    _  | | |\/| |  _|   | | |  _| | |_) |       &lt;br /&gt; / ___ \|  __/ ___ \ |___|  _  | |___  | |_| | |  | | |___  | | | |___|  _ &amp;lt;       &lt;br /&gt;/_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.4.3       &lt;br /&gt;       &lt;br /&gt;Copyright (c) 1999-2021 The Apache Software Foundation       &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;显示了JMeter的Logo和版本说明JMeter可以正常运行了。   &lt;h2&gt;    &lt;a href="https://devopstools.cn/#Jenkins&amp;#23433;&amp;#35013;Performance-plugin" title="Jenkins&amp;#23433;&amp;#35013;Performance plugin"&gt;&lt;/a&gt;Jenkins安装Performance plugin&lt;/h2&gt;进入Jenkins以后通过【Manage Jenkins】-[Mangage Plugins]   &lt;br /&gt;在 Available 标签中查找Performance Plugin，并选择该插件进行安装，安装后重启Jenkins   &lt;br /&gt;   &lt;img alt="Performance Plugin" src="https://devopstools.cn/images/Jenkins-Performance-Plugin.png"&gt;&lt;/img&gt;   &lt;h2&gt;    &lt;a href="https://devopstools.cn/#&amp;#32534;&amp;#20889;&amp;#19968;&amp;#20010;JMeter&amp;#24615;&amp;#33021;&amp;#27979;&amp;#35797;&amp;#22330;&amp;#26223;" title="&amp;#32534;&amp;#20889;&amp;#19968;&amp;#20010;JMeter&amp;#24615;&amp;#33021;&amp;#27979;&amp;#35797;&amp;#22330;&amp;#26223;"&gt;&lt;/a&gt;编写一个JMeter性能测试场景&lt;/h2&gt;我们通过一个录制的方法编写一个性能测试场景。以GUI模式运行JMeter，创建一个测试计划   &lt;code&gt;DevOpsToolsTestPlan&lt;/code&gt;,测试计划中添加一个线程组（   &lt;code&gt;Thread Group&lt;/code&gt;）和2 个HTTP Request&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;HTTPS 请求访问  &lt;code&gt;https://devopstools.cn&lt;/code&gt;  &lt;br /&gt;HTTPS 请求访问  &lt;code&gt;https://devopstools.cn/2021/11/08/bdd/cucumber-java-ct/&lt;/code&gt;  &lt;br /&gt;  &lt;img alt="HTTP Request" src="https://devopstools.cn/images/JMeter-HTTPRequest.png"&gt;&lt;/img&gt;  &lt;br /&gt;GUI中点击运行【Run】按钮，测试脚本是否正常。  &lt;br /&gt;  &lt;img alt="Run Test Plan" src="https://devopstools.cn/images/JMeter-RunDevOpsToolsTestPlan.png"&gt;&lt;/img&gt;  &lt;br /&gt;最后把性能测试脚本上传到Git仓库，Jenkins中访问Git仓库实现加载测试计划并运行测试计划。  &lt;br /&gt;Git仓库中性能测试仓库地址为：&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;https://gitee.com/devopstools/jenkins-jmeter-demo.git     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#Jenkins&amp;#20013;&amp;#21019;&amp;#24314;&amp;#27969;&amp;#27700;&amp;#32447;" title="Jenkins&amp;#20013;&amp;#21019;&amp;#24314;&amp;#27969;&amp;#27700;&amp;#32447;"&gt;&lt;/a&gt;Jenkins中创建流水线&lt;/h2&gt; &lt;p&gt;Jenkins中创建流水线工程名称为Jenkins-JMeter-Demo。&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;1     &lt;br /&gt;2     &lt;br /&gt;3     &lt;br /&gt;4     &lt;br /&gt;5     &lt;br /&gt;6     &lt;br /&gt;7     &lt;br /&gt;8     &lt;br /&gt;9     &lt;br /&gt;10     &lt;br /&gt;11     &lt;br /&gt;12     &lt;br /&gt;13     &lt;br /&gt;14     &lt;br /&gt;15     &lt;br /&gt;16     &lt;br /&gt;17     &lt;br /&gt;18     &lt;br /&gt;19     &lt;br /&gt;20     &lt;br /&gt;21     &lt;br /&gt;22     &lt;br /&gt;23     &lt;br /&gt;24     &lt;br /&gt;25     &lt;br /&gt;26     &lt;br /&gt;27     &lt;br /&gt;28     &lt;br /&gt;29     &lt;br /&gt;30     &lt;br /&gt;31     &lt;br /&gt;32     &lt;br /&gt;33     &lt;br /&gt;34     &lt;br /&gt;35     &lt;br /&gt;36     &lt;br /&gt;37     &lt;br /&gt;38     &lt;br /&gt;39     &lt;br /&gt;40     &lt;br /&gt;41     &lt;br /&gt;42     &lt;br /&gt;43     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;pipeline {     &lt;br /&gt;    agent any     &lt;br /&gt;         &lt;br /&gt;    tools{     &lt;br /&gt;        git &amp;apos;Default&amp;apos;     &lt;br /&gt;    }     &lt;br /&gt;         &lt;br /&gt;    options { timeout(time: 2, unit: &amp;apos;HOURS&amp;apos;) }     &lt;br /&gt;         &lt;br /&gt;     &lt;br /&gt;         &lt;br /&gt;    stages {     &lt;br /&gt;             &lt;br /&gt;        stage(&amp;apos;获取代码&amp;apos;){     &lt;br /&gt;            steps{     &lt;br /&gt;                echo &amp;apos;---SCM---&amp;apos;     &lt;br /&gt;                git credentialsId: &amp;apos;devopstools&amp;apos;, url: &amp;apos;https://gitee.com/devopstools/jenkins-jmeter-demo.git&amp;apos;     &lt;br /&gt;            }     &lt;br /&gt;        }     &lt;br /&gt;             &lt;br /&gt;           &lt;br /&gt;     &lt;br /&gt;            &lt;br /&gt;        stage(&amp;quot;性能测试&amp;quot;) {     &lt;br /&gt;            steps {     &lt;br /&gt;              echo &amp;apos;---PerformanceTest---&amp;apos;     &lt;br /&gt;               sh &amp;quot;pwd&amp;quot;     &lt;br /&gt;               sh &amp;quot;/var/jenkins_home/tools/apache-jmeter-5.4.3/bin/jmeter -j jmeter.save.saveservice.output_format=xml -n -Jthreadcount=5 -t DevOpsToolsTestPlan.jmx -l reports/DevOpsToolsTestPlan.report.jtl&amp;quot;     &lt;br /&gt;                   &lt;br /&gt;            }     &lt;br /&gt;        }        &lt;br /&gt;             &lt;br /&gt;       stage(&amp;quot;性能测试报告&amp;quot;) {     &lt;br /&gt;        steps {     &lt;br /&gt;          echo &amp;apos;---Performance Reports---&amp;apos;     &lt;br /&gt;          perfReport filterRegex: &amp;apos;&amp;apos;, showTrendGraphs: true, sourceDataFiles: &amp;apos;reports/*.jtl&amp;apos;     &lt;br /&gt;               &lt;br /&gt;        }     &lt;br /&gt;     }      &lt;br /&gt;         &lt;br /&gt;    }     &lt;br /&gt;}     &lt;br /&gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;运行Jenkins流水线可以查看到性能测试结果  &lt;br /&gt;Jenkins上查看性能趋势图  &lt;br /&gt;  &lt;img alt="&amp;#24615;&amp;#33021;&amp;#36235;&amp;#21183;&amp;#22270;" src="https://devopstools.cn/images/Jenkins-JMeter-Graph01.png"&gt;&lt;/img&gt;  &lt;br /&gt;Jenkins上查看性能指标  &lt;br /&gt;  &lt;img alt="&amp;#24615;&amp;#33021;&amp;#25351;&amp;#26631;" src="https://devopstools.cn/images/Jenkins-JMeter-ResultTable.png"&gt;&lt;/img&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>CI/CD Jenkins 持续测试 性能测试 JMeter</category>
      <guid isPermaLink="true">https://itindex.net/detail/62077-jenkins-jmeter-%E6%80%A7%E8%83%BD</guid>
      <pubDate>Sun, 30 Jan 2022 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>如何使用 MockServer 自动测试 Spring Boot API</title>
      <link>https://itindex.net/detail/61947-mockserver-%E6%B5%8B%E8%AF%95-spring</link>
      <description>&lt;div&gt;  &lt;div&gt;   &lt;p&gt;作为应用程序开发的一部分，系统集成是不可避免的。无论您的系统是一组 现代微服务还是遗留的单体系统，大多数系统逻辑都依赖于其他流程或数据源，以提供有意义的业务功能。集成将采用库函数调用或 REST API 调用的形式。与其他组件的接口通常令人头疼，尤其是在系统开发期间您的对应组件无法进行测试时。它是对接口逻辑的质量保证的约束。因此，在开发周期的后期会发现一些系统错误，并可能阻碍系统交付的进度。&lt;/p&gt;   &lt;p&gt;长期以来，开发人员一直在为这个问题苦苦挣扎。过去，每个新开发项目都计划进行一定量的开发工作来构建模拟外部服务响应的存根，以便在没有这些依赖关系的情况下进行开发和测试。&lt;/p&gt;   &lt;p&gt;例如，人们在接口组件中构建了一个适配器，用于从“离线”文件读取模拟数据响应。这样的适配器不是虚拟存根，开发是一项耗时且乏味的任务，因为它包含以专有格式解析消息并识别请求类型，然后查找并返回相应模拟响应的逻辑。这是银行业的常见做法，因为核心银行系统 AS/400 或大型机并不总是可用于通过银行门户进行测试。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;div&gt;        &lt;img alt="" height="250" src="https://miro.medium.com/max/60/1*aabr5yHNVWaV3XyHxmdBGQ.jpeg?q=20" width="694"&gt;&lt;/img&gt;&lt;/div&gt;       &lt;img alt="" height="250" src="https://miro.medium.com/max/1388/1*aabr5yHNVWaV3XyHxmdBGQ.jpeg" width="694"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;具有用于测试的专用离线模式适配器的金融系统   &lt;p&gt;如今，测试框架和模拟工具的普遍使用帮助我们更轻松地处理模拟。结合 Maven 等开发实用程序，大多数测试都可以自动化，从而大大加快开发和测试过程。在本文中，我将分享如何集成    &lt;a href="https://www.mock-server.com/" rel="noopener ugc nofollow" target="_blank"&gt;MockServer&lt;/a&gt;并自动化 Java Spring Boot API 开发的单元测试和集成测试。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h1&gt;什么是模拟服务器？&lt;/h1&gt;   &lt;p&gt;您需要一个模拟 REST API 服务来验证整个开发周期中系统的功能和集成逻辑。但是，外部服务可能不可用，如果并行开发外部服务，通常会发生这种情况。    &lt;a href="https://www.mock-server.com/" rel="noopener ugc nofollow" target="_blank"&gt;MockServer&lt;/a&gt;是一个有用的工具，它提供了一种模拟任何 REST API 服务的简单方法。它提供了开发人员友好的功能，例如 maven 插件和用于测试配置的注释。此外，它还支持 OpenAPI（又名 Swagger）定义、Java API 以及用于设置请求期望和模拟响应的 Javascript API。&lt;/p&gt;   &lt;p&gt;它是一个独立的过程，它为一组期望的设置提供管理接口，即期望请求和模拟响应的映射。您可以调用其管理 REST API 或使用 MockServerClient（Java 或 JS 版本）来创建期望。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;img alt="" height="384" src="https://miro.medium.com/max/1400/1*VAyzGQpg_psV_aWapr6c9w.png" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;MockServer 请求流来自 (    &lt;a href="https://www.mock-server.com/#what-is-mockserver" rel="noopener ugc nofollow" target="_blank"&gt;https://www.mock-server.com/#what-is-mockserver&lt;/a&gt; )   &lt;p&gt;与普通模拟响应类似，错误响应可以通过设置映射到错误响应的请求匹配器来模拟。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;div&gt;        &lt;img alt="" height="386" src="https://miro.medium.com/max/60/1*gkjCC-C75jGczwP1xQ6T2g.png?q=20" width="700"&gt;&lt;/img&gt;&lt;/div&gt;       &lt;img alt="" height="386" src="https://miro.medium.com/max/1400/1*gkjCC-C75jGczwP1xQ6T2g.png" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;MockServer 请求流来自 (    &lt;a href="https://www.mock-server.com/#what-is-mockserver" rel="noopener ugc nofollow" target="_blank"&gt;https://www.mock-server.com/#what-is-mockserver&lt;/a&gt; )   &lt;p&gt;下图显示了对客户服务的期望。为适应不同的测试场景，可以为正常情况配置模拟响应的映射，有 200 个状态和错误情况，例如 404 错误和 500 错误。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;div&gt;        &lt;img alt="" height="384" src="https://miro.medium.com/max/60/1*Hy8jx65JV4JdpQmJLftpRg.jpeg?q=20" width="700"&gt;&lt;/img&gt;&lt;/div&gt;       &lt;img alt="" height="384" src="https://miro.medium.com/max/1400/1*Hy8jx65JV4JdpQmJLftpRg.jpeg" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;设定对客户服务的期望   &lt;p&gt;配置了一组期望后，MockServer 会查找传入请求的一组期望，并在测试执行期间返回相应的模拟响应。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;img alt="" height="243" src="https://miro.medium.com/max/60/1*TtKaxVhOsGg6cyWW4tRG2w.jpeg?q=20" width="507"&gt;&lt;/img&gt;&lt;/div&gt;      &lt;img alt="" height="243" src="https://miro.medium.com/max/1014/1*TtKaxVhOsGg6cyWW4tRG2w.jpeg" width="507"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;MockServer 根据配置的期望返回模拟响应&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h1&gt;报价API&lt;/h1&gt;   &lt;p&gt;为了更好地说明这些概念，我将向您展示一个简单的保险报价 API 的实现。系统根据客户的要求生成报价建议。为了生成报价，业务规则在报价服务中编码，该服务协调和使用外部服务的 REST API——客户、产品和报价引擎。&lt;/p&gt;   &lt;div&gt;    &lt;img alt="" height="345" src="https://miro.medium.com/max/1316/1*JPZNo7s6cHicWyW5yr3sFQ.jpeg" width="658"&gt;&lt;/img&gt;&lt;/div&gt;报价API   &lt;p&gt;下面的流程图显示了报价 API 的逻辑，它验证客户资料和产品规格，然后调用报价引擎 API。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;div&gt;        &lt;img alt="" height="223" src="https://miro.medium.com/max/60/1*TNHUHVMjHex70DIQ0hRt5Q.png?q=20" width="700"&gt;&lt;/img&gt;&lt;/div&gt;       &lt;img alt="" height="223" src="https://miro.medium.com/max/1400/1*TNHUHVMjHex70DIQ0hRt5Q.png" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;报价服务逻辑流程   &lt;h2&gt;组件结构&lt;/h2&gt;   &lt;p&gt;除非你的系统超级简单，否则强烈建议定义一个 API 客户端来处理每个外部服务的 HTTP 通信和数据转换。这种设计有助于松耦合实现，使系统易于维护，以便将来进行故障排除和可能的修改。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;img alt="" height="257" src="https://miro.medium.com/max/60/1*vgDm7H76BKMnxFqmZ2BS1Q.png?q=20" width="663"&gt;&lt;/img&gt;&lt;/div&gt;      &lt;img alt="" height="257" src="https://miro.medium.com/max/1326/1*vgDm7H76BKMnxFqmZ2BS1Q.png" width="663"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;使用 API 客户端与外部 API 集成   &lt;h2&gt;API 客户端&lt;/h2&gt;   &lt;p&gt;API 客户端负责与外部 REST API 的集成，例如 HTTP 协议请求和响应、转换错误状态代码以及数据转换。这是用于客户服务的 API 客户端的示例&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h2&gt;Git 存储库&lt;/h2&gt;   &lt;p&gt;参考这个GitHub存储库（    &lt;a href="https://github.com/gavinklfong/spring-mock-demo.git" rel="noopener ugc nofollow" target="_blank"&gt;https://github.com/gavinklfong/spring-mock-demo.git&lt;/a&gt;）获取带有自动化测试实现的报价API的完整源代码&lt;/p&gt;   &lt;p&gt;运行 maven command     &lt;code&gt;mvn clean install&lt;/code&gt;，它将自动构建示例应用程序并使用 MockServer 运行测试。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h1&gt;运行 MockServer 进行单元测试&lt;/h1&gt;   &lt;p&gt;MockServer 支持许多选项来运行服务器，例如 Java API、JUnit 扩展、Docker 容器等。在这个例子中，我们使用 Java 注释运行 MockServer    &lt;code&gt;@MockServerTest&lt;/code&gt;进行单元测试。&lt;/p&gt;   &lt;p&gt;您需要在 Maven pom.xml 中包含以下依赖项设置。&lt;/p&gt;   &lt;pre&gt;&amp;lt;dependency&amp;gt;     &lt;br /&gt;&amp;lt;groupId&amp;gt;org.mock-server&amp;lt;/groupId&amp;gt;     &lt;br /&gt;&amp;lt;artifactId&amp;gt;mockserver-spring-test-listener&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;&amp;lt;version&amp;gt;5.11.1&amp;lt;/version&amp;gt;     &lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;/pre&gt;   &lt;p&gt;在执行单元测试用例时，注释会在可用端口上启动 MockServer。MockServerClient 将自动注入测试类，因此您可以在测试执行之前向 MockServer 发送指令以设置模拟响应。每个测试用例都应该有自己的一组模拟响应配置，因为 MockServer 在每个测试用例执行之前被重置。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h2&gt;使用 OpenAPI 模拟期望设置&lt;/h2&gt;   &lt;p&gt;接下来，让我们演示如何使用 OpenAPI 定义（又名 Swagger）配置期望。OpenAPI 定义的支持改变了游戏规则，因为使用 OpenAPI 规范是 REST API 开发的常见做法。大多数服务提供商将其用作其 API 服务的接口规范。利用 OpenAPI 规范作为模拟响应设置可以为您节省大量的测试用例开发工作。&lt;/p&gt;   &lt;p&gt;模拟响应可以作为 API 规范的一部分嵌入，您可以将其指定为示例数据。这是    &lt;code&gt;GET /customers/{id}&lt;/code&gt;带有示例数据的端点的示例 OpenAPI 规范。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;p&gt;此示例代码将 OpenAPI 规范导入 MockServer，并指示 MockServer 为操作 ID 为“getCustomerById”的 API 端点设置 200 状态的模拟响应。您可以通过在方法调用中指定更多操作 id 来要求 MockServer 为多个 API 端点设置期望。&lt;/p&gt;   &lt;p&gt;当调用客户 API 客户端以通过 id 获取客户时，它会将 API 请求提交给返回模拟响应的 MockServer。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h2&gt;使用 Java 代码模拟期望设置&lt;/h2&gt;   &lt;p&gt;或者，您可以使用 MockServerClient 的方法调用来开发模拟响应。代码简单明了，代码模式类似于 Mockito 的模拟。此示例展示了如何为 API 设置模拟响应以通过 id 检索产品 — [GET /products/{id}]&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h2&gt;报价服务单元测试&lt;/h2&gt;   &lt;p&gt;虽然 Quotation 服务的单元测试不需要 MockServer，但不要忘记为 Quotation 服务开发测试。测试用例的实现可以简单地通过为所有 API 客户端注入模拟 bean 来完成。报价服务与系统逻辑验证的模拟交互。&lt;/p&gt;   &lt;div&gt;    &lt;img alt="" height="397" src="https://miro.medium.com/max/1026/1*LeD_eUJMvfZTBASN_OseVg.png" width="513"&gt;&lt;/img&gt;&lt;/div&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h1&gt;集成测试怎么样？&lt;/h1&gt;   &lt;p&gt;一旦通过了所有单元测试，就可以将所有组件放在一起，并在集成测试中端到端地验证系统逻辑。系统边界是报价API，覆盖报价服务和所有API客户端，而所有外部服务都使用MockServer模拟。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;img alt="" height="427" src="https://miro.medium.com/max/1400/1*67psUTut7JSPdHZBWv-g5A.jpeg" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;集成测试设置   &lt;h1&gt;使用 Maven 生命周期运行集成测试&lt;/h1&gt;   &lt;p&gt;Maven 为集成测试的执行提供了一种便捷的方式。Maven 阶段称为预集成测试，用于在集成测试阶段执行测试用例之前初始化服务和应用程序。在集成测试结束时，maven 将运行post-integration-test阶段以关闭 Spring Boot 应用程序和 MockServer。&lt;/p&gt;   &lt;p&gt;在预集成-测试阶段，我们首先使用 maven 插件build-helper-maven-plugin为 Spring Boot 应用程序和 MockServer 预留可用的网络端口，然后使用 maven 插件mockserver-maven-plugin和spring-boot-maven-plugin分别。&lt;/p&gt;   &lt;p&gt;这种 Maven 配置对于测试自动化和 CI/CD 管道至关重要。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;div&gt;       &lt;div&gt;        &lt;img alt="" height="239" src="https://miro.medium.com/max/60/1*FhyOxmDxXEVWUHZIzSMAMQ.jpeg?q=20" width="700"&gt;&lt;/img&gt;&lt;/div&gt;       &lt;img alt="" height="239" src="https://miro.medium.com/max/1400/1*FhyOxmDxXEVWUHZIzSMAMQ.jpeg" width="700"&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;用于集成测试的 Maven 生命周期   &lt;h2&gt;网络端口预留的Maven插件设置&lt;/h2&gt;   &lt;p&gt;maven 插件会寻找 2 个可用的端口，并在pre-integration-test阶段设置为变量    &lt;em&gt;spring-boot.http.port&lt;/em&gt;和    &lt;em&gt;mock-server.http.port&lt;/em&gt;&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h2&gt;MockServer 启动的 Maven 插件设置&lt;/h2&gt;   &lt;p&gt;下面的 maven 插件配置使用保留的端口号启动 MockServer。为了在启动时初始化 MockServer 中的所有期望，指定一个 java 类，    &lt;code&gt;&amp;lt;initializationClass&amp;gt;&lt;/code&gt;其中包含请求和模拟响应的映射列表。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;p&gt;初始化类实现了一个调用的接口    &lt;code&gt;PluginExpectationInitializer&lt;/code&gt;并    &lt;code&gt;initializeExpectations()&lt;/code&gt;包含所有模拟响应设置。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h2&gt;Spring Boot 启动的 Maven 插件设置&lt;/h2&gt;   &lt;p&gt;为了指定端口号并覆盖 Spring Boot application.yml 中所有外部服务的 URL，该插件使用参数列表启动 Spring Boot。Spring Boot 应用程序将连接到 MockServer 以与外部服务（客户、产品和报价引擎）集成。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;   &lt;h1&gt;集成测试用例&lt;/h1&gt;   &lt;p&gt;随着 Spring Boot 应用程序和 MockServer 的启动和运行，测试用例将简单地运行一个 Web 客户端来触发对报价服务的 HTTP 请求以生成报价并验证结果。&lt;/p&gt;   &lt;div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;h1&gt;最后的想法&lt;/h1&gt;   &lt;p&gt;无论您是构建大型企业系统还是小型服务，    &lt;a href="https://www.bacancytechnology.com/blog/rest-api-best-practices" rel="noopener ugc nofollow" target="_blank"&gt;REST API 集成&lt;/a&gt;都是大多数系统架构中的常见模式。因此，REST API 模拟服务器已成为验证和质量保证的重要组成部分。&lt;/p&gt;   &lt;p&gt;MockServer 是 Java Spring Boot API 开发的最佳选择，它是一个强大的工具，它为自动化测试用例设置提供了许多选项，例如 Java API、JUnit 扩展、Spring 测试执行侦听器、Docker 等。 模拟数据配置简单明了，你可以像 Mockito 风格的模拟那样做，也可以简单地使用 OpenAPI 规范。&lt;/p&gt;   &lt;p&gt;它与 Maven 无缝协作，使得创建自动化管道以使用 MockServer 运行单元测试和集成测试变得更加容易。&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61947-mockserver-%E6%B5%8B%E8%AF%95-spring</guid>
      <pubDate>Fri, 10 Dec 2021 11:48:10 CST</pubDate>
    </item>
    <item>
      <title>Spring Cloud Contract 契约测试简介</title>
      <link>https://itindex.net/detail/61938-spring-cloud-contract</link>
      <description>&lt;h2&gt;一、介绍  &lt;a href="https://www.baeldung.com/spring-cloud-contract#introduction"&gt;&lt;/a&gt;&lt;/h2&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;  &lt;a href="https://cloud.spring.io/spring-cloud-contract/" rel="noopener"&gt;Spring Cloud Contract&lt;/a&gt;是一个项目，简单地说，就是帮助我们编写  &lt;a href="https://martinfowler.com/articles/consumerDrivenContracts.html" rel="noopener"&gt;消费者驱动的合同（CDC）&lt;/a&gt;。&lt;/p&gt; &lt;p&gt;这确保了分布式系统中  &lt;em&gt;Producer&lt;/em&gt;和  &lt;em&gt;Consumer&lt;/em&gt;之间的契约——用于基于 HTTP 和基于消息的交互。&lt;/p&gt; &lt;p&gt;在这篇快速文章中，我们将探索通过 HTTP 交互为 Spring Cloud Contract 编写生产者和消费者端测试用例。&lt;/p&gt; &lt;h2&gt;2. 生产者 – 服务器端  &lt;a href="https://www.baeldung.com/spring-cloud-contract#producer-start"&gt;&lt;/a&gt;&lt;/h2&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;我们将编写一个生产者端 CDC，以  &lt;em&gt;EvenOddController&lt;/em&gt;的形式——它只是告诉  &lt;em&gt;数字&lt;/em&gt;参数是偶数还是奇数：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@RestController
public class EvenOddController {

    @GetMapping(&amp;quot;/validate/prime-number&amp;quot;)
    public String isNumberPrime(@RequestParam(&amp;quot;number&amp;quot;) Integer number) {
        return Integer.parseInt(number) % 2 == 0 ? &amp;quot;Even&amp;quot; : &amp;quot;Odd&amp;quot;;
    }
}&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;2.1. Maven 依赖项  &lt;a href="https://www.baeldung.com/spring-cloud-contract#producer-dependency"&gt;&lt;/a&gt;&lt;/h3&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;对于我们的生产者方面，我们需要  &lt;a href="https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-starter-contract-verifier%22" rel="noopener"&gt;   &lt;em&gt;spring-cloud-starter-contract-verifier&lt;/em&gt;&lt;/a&gt;依赖项：&lt;/p&gt; &lt;div&gt;  &lt;div align="center"&gt;   &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;pre&gt;  &lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-cloud-starter-contract-verifier&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.1.1.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;我们需要使用我们的基本测试类的名称配置  &lt;a href="https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-contract-maven-plugin%22" rel="noopener"&gt;   &lt;em&gt;spring-cloud-contract-maven-plugin&lt;/em&gt;&lt;/a&gt;，我们将在下一节中描述：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-cloud-contract-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.1.1.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;extensions&amp;gt;true&amp;lt;/extensions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;baseClassForTests&amp;gt;
            com.baeldung.spring.cloud.springcloudcontractproducer.BaseTestClass
        &amp;lt;/baseClassForTests&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;2.2. 生产者端设置  &lt;a href="https://www.baeldung.com/spring-cloud-contract#producer-setup"&gt;&lt;/a&gt;&lt;/h3&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;我们需要在加载 Spring 上下文的测试包中添加一个基类：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@DirtiesContext
@AutoConfigureMessageVerifier
public class BaseTestClass {

    @Autowired
    private EvenOddController evenOddController;

    @Before
    public void setup() {
        StandaloneMockMvcBuilder standaloneMockMvcBuilder 
          = MockMvcBuilders.standaloneSetup(evenOddController);
        RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder);
    }
}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;在  &lt;em&gt;/src/test/resources/contracts/&lt;/em&gt;包中，我们将添加测试存根，例如文件  &lt;em&gt;shouldReturnEvenWhenRequestParamIsEven.groovy&lt;/em&gt;中的  &lt;em&gt;这个&lt;/em&gt;：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;import org.springframework.cloud.contract.spec.Contract
Contract.make {
    description &amp;quot;should return even when number input is even&amp;quot;
    request{
        method GET()
        url(&amp;quot;/validate/prime-number&amp;quot;) {
            queryParameters {
                parameter(&amp;quot;number&amp;quot;, &amp;quot;2&amp;quot;)
            }
        }
    }
    response {
        body(&amp;quot;Even&amp;quot;)
        status 200
    }
}
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;当我们运行构建时，插件会自动生成一个名为  &lt;em&gt;ContractVerifierTest&lt;/em&gt;的测试类，它扩展了我们的  &lt;em&gt;BaseTestClass&lt;/em&gt;并将其放在  &lt;em&gt;/target/generated-test-sources/contracts/ 中&lt;/em&gt;。&lt;/p&gt; &lt;p&gt;测试方法的名称源自前缀“   &lt;em&gt;validate_”&lt;/em&gt;与我们的 Groovy 测试存根的名称连接。对于上述 Groovy 文件，生成的方法名称将为  &lt;em&gt;“validate_shouldReturnEvenWhenRequestParamIsEven”&lt;/em&gt;。&lt;/p&gt; &lt;div&gt;  &lt;div align="center"&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;a href="https://freestar.com/?utm_campaign=branding&amp;utm_medium=&amp;utm_source=baeldung.com&amp;utm_content=baeldung_leaderboard_mid_2" rel="noreferrer" target="_blank"&gt;       &lt;img alt="&amp;#33258;&amp;#30001;&amp;#20043;&amp;#26143;" height="14" src="https://a.pub.network/core/imgs/fslogo-green.svg" width="14"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;让我们看看这个自动生成的测试类：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;public class ContractVerifierTest extends BaseTestClass {

@Test
public void validate_shouldReturnEvenWhenRequestParamIsEven() throws Exception {
    // given:
    MockMvcRequestSpecification request = given();

    // when:
    ResponseOptions response = given().spec(request)
      .queryParam(&amp;quot;number&amp;quot;,&amp;quot;2&amp;quot;)
      .get(&amp;quot;/validate/prime-number&amp;quot;);

    // then:
    assertThat(response.statusCode()).isEqualTo(200);
    
    // and:
    String responseBody = response.getBody().asString();
    assertThat(responseBody).isEqualTo(&amp;quot;Even&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;构建还将在我们的本地 Maven 存储库中添加存根 jar，以便我们的使用者可以使用它。&lt;/p&gt; &lt;p&gt;存根将出现在  &lt;em&gt;stubs/mapping/&lt;/em&gt;下的输出文件夹中。&lt;/p&gt; &lt;h2&gt;3. 消费者 – 客户端  &lt;a href="https://www.baeldung.com/spring-cloud-contract#consumer-start"&gt;&lt;/a&gt;&lt;/h2&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;我们CDC的消费者端会通过HTTP交互消费生产者端生成的stub来维护合约，所以生产者端的任何变化都会破坏合约。&lt;/p&gt; &lt;p&gt;我们将添加  &lt;em&gt;BasicMathController，&lt;/em&gt;它将发出 HTTP 请求以从生成的存根中获取响应：&lt;/p&gt; &lt;div&gt;  &lt;div align="center"&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;a href="https://freestar.com/?utm_campaign=branding&amp;utm_medium=banner&amp;utm_source=baeldung.com&amp;utm_content=baeldung_leaderboard_mid_3" rel="noreferrer" target="_blank"&gt;       &lt;img alt="&amp;#33258;&amp;#30001;&amp;#20043;&amp;#26143;" height="14" src="https://a.pub.network/core/imgs/fslogo-green.svg" width="14"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;pre&gt;  &lt;code&gt;@RestController
public class BasicMathController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(&amp;quot;/calculate&amp;quot;)
    public String checkOddAndEven(@RequestParam(&amp;quot;number&amp;quot;) Integer number) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add(&amp;quot;Content-Type&amp;quot;, &amp;quot;application/json&amp;quot;);

        ResponseEntity&amp;lt;String&amp;gt; responseEntity = restTemplate.exchange(
          &amp;quot;http://localhost:8090/validate/prime-number?number=&amp;quot; + number,
          HttpMethod.GET,
          new HttpEntity&amp;lt;&amp;gt;(httpHeaders),
          String.class);

        return responseEntity.getBody();
    }
}&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;3.1. Maven 依赖项  &lt;a href="https://www.baeldung.com/spring-cloud-contract#consumer-dependency"&gt;&lt;/a&gt;&lt;/h3&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;对于我们的消费者，我们需要添加  &lt;a href="https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-contract-wiremock%22"&gt;   &lt;em&gt;spring-cloud-contract-wiremock&lt;/em&gt;&lt;/a&gt;和  &lt;a href="https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.springframework.cloud%22%20AND%20a%3A%22spring-cloud-contract-stub-runner%22"&gt;   &lt;em&gt;spring-cloud-contract-stub-runner&lt;/em&gt;&lt;/a&gt;依赖项：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-cloud-contract-wiremock&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.1.1.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.cloud&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-cloud-contract-stub-runner&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;2.1.1.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt; &lt;h3&gt;3.2. 消费者端设置  &lt;a href="https://www.baeldung.com/spring-cloud-contract#consumer-setup"&gt;&lt;/a&gt;&lt;/h3&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;现在是配置我们的存根运行器的时候了，它将通知我们的消费者我们本地 Maven 存储库中的可用存根：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
@AutoConfigureJsonTesters
@AutoConfigureStubRunner(
  stubsMode = StubRunnerProperties.StubsMode.LOCAL,
  ids = &amp;quot;com.baeldung.spring.cloud:spring-cloud-contract-producer:+:stubs:8090&amp;quot;)
public class BasicMathControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven()
      throws Exception {
 
        mockMvc.perform(MockMvcRequestBuilders.get(&amp;quot;/calculate?number=2&amp;quot;)
          .contentType(MediaType.APPLICATION_JSON))
          .andExpect(status().isOk())
          .andExpect(content().string(&amp;quot;Even&amp;quot;));
    }
}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;请注意，  &lt;em&gt;@AutoConfigureStubRunner&lt;/em&gt;注释的  &lt;em&gt;ids&lt;/em&gt;属性指定：  &lt;em&gt;&lt;/em&gt;&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;em&gt;com.baeldung.spring.cloud&lt;/em&gt; —我们的工件的   &lt;em&gt;groupId&lt;/em&gt;&lt;/li&gt;  &lt;li&gt;   &lt;em&gt;spring-cloud-contract-producer&lt;/em&gt; —生产者存根 jar的   &lt;em&gt;artifactId&lt;/em&gt;&lt;/li&gt;  &lt;li&gt;   &lt;em&gt;8090&lt;/em&gt; — 生成的存根将在其上运行的端口&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;4. 合同违约时  &lt;a href="https://www.baeldung.com/spring-cloud-contract#broken-contract"&gt;&lt;/a&gt;&lt;/h2&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;如果我们在生产者端进行任何直接影响合约的更改而不更新消费者端，这可能会导致合约失败。&lt;/p&gt; &lt;p&gt;例如，假设我们要将生产者端的  &lt;em&gt;EvenOddController&lt;/em&gt;请求 URI  &lt;em&gt;更改&lt;/em&gt;为  &lt;em&gt;/validate/change/prime-number&lt;/em&gt;。&lt;/p&gt; &lt;p&gt;如果我们没有通知我们的消费者这个变化，消费者仍然会将它的请求发送到  &lt;em&gt;/validate/prime-number&lt;/em&gt; URI，消费者端的测试用例将抛出  &lt;em&gt;org.springframework.web.client.HttpClientErrorException: 404 Not Found&lt;/em&gt;。&lt;/p&gt; &lt;h2&gt;5. 总结  &lt;a href="https://www.baeldung.com/spring-cloud-contract#summary"&gt;&lt;/a&gt;&lt;/h2&gt; &lt;div&gt;&lt;/div&gt; &lt;p&gt;我们已经看到 Spring Cloud Contract 如何帮助我们维护服务消费者和生产者之间的契约，以便我们可以推出新代码而不必担心破坏契约。&lt;/p&gt; &lt;p&gt;而且，一如既往，可以  &lt;a href="https://github.com/eugenp/tutorials/tree/master/spring-cloud/spring-cloud-contract"&gt;在 GitHub 上&lt;/a&gt;找到本教程的完整实现。&lt;/p&gt;
     
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61938-spring-cloud-contract</guid>
      <pubDate>Mon, 06 Dec 2021 14:23:55 CST</pubDate>
    </item>
    <item>
      <title>微服务自动化测试的测试策略 - Web 3.0 Cloud-Streams 产品级敏捷</title>
      <link>https://itindex.net/detail/61934-%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;div&gt;    &lt;div&gt;   &lt;br /&gt;&lt;/div&gt;    &lt;h3&gt;前言:&lt;/h3&gt;    &lt;p&gt;微服务遵循著单一责任 (Single Responsibility) 的设计原则, 使得微服务较传统的单体 (Monolithic) 能更容易的独立发布、部署。另一方面, 微服务能拥有更大的空间去选择适合自身的编程语言、技术。最重要的一点是, 微服务的架构更容易的能做到 “水平扩展”。&lt;/p&gt;    &lt;p&gt;然而, 微服务也有它的技术挑战需要克服。&lt;/p&gt;    &lt;p&gt;如图一所示, 微服务的架构是分布式的, 在这样的分布式的架构下, 所谓的 “自动化测试” 将是微服务能否成功的一个首要且关键的要素。&lt;/p&gt;    &lt;p&gt;我们将以一系列的文章来探讨微服务自动化测试的策略、方法、工具。首先, 我们从微服务的测试策略谈起。&lt;/p&gt;    &lt;img alt="" height="1020" src="https://www.deva9.com/wp-content/uploads/2018/08/ms.png" width="1288"&gt;&lt;/img&gt;图一：微服务; 分布式的架构    &lt;h3&gt;本文:　&lt;/h3&gt;    &lt;p&gt;　在谈微服务的自动化测试策略前, 我们需先一起共同的来探讨: 微服务内的架构、微服务与微服务间的协作。然后, 我们就可以探讨微服务需要那些类型的自动化测试。&lt;/p&gt;    &lt;img alt="" height="650" src="https://www.deva9.com/wp-content/uploads/2018/08/servicesdetail.png" width="746"&gt;&lt;/img&gt;图二: 微服务内部的主要元素    &lt;p&gt;如图二所示, 微服务的内部主要是由以下的元素所组成：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;Resources&lt;/strong&gt;: 主要的责任是经由所选择的协议; 如: REST; 将微服务所提供的服务暴露给微服务的 Client。Resource 会完整性的校验由微服务的 Client所传过来的请求 (Request), 并且将微服务执行完某个服务后的结果, 按照由协议所界定的响应 (Response) 格式; 如: JSON; 提供给微服务的 Client。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;Service layer:&lt;/strong&gt;主要的责任是派工给多个的 Domain, 以共同的完成微服务Client 的请求。Resource 完整的校验由微服务的 Client 所传过来的请求(Request) 后, 假如, 此请求会需要自身微服务内部或外部的微服务多个的Domain 才能完成时, Resource 便会将此请求, 交由 Service layer 来处理。Service layer 便会派工给多个的 Domain。当此请求需要调用到外部的微服务时, Service layer 便会藉由 Gateways去传递个请求 (Request) 到外部的微服务。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;Domain&lt;/strong&gt;: 主要的责任是专注在微服务业务逻辑的处理。Domain 会将处理完业务逻辑后的结果, 传回给 Resources。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;Gateways: &lt;/strong&gt;主要的责任是使有关连; 会发生调用; 的微服务可以连接起来。Gateways 引导著来自自身微服务的 Service layer 的请求 (Request), 到另一个或多个的外部的微服务, 并且将外部的微服务的响应 (Response) , 传回给自身微服务的 Domain。Gateways 则是藉由 HTTP client 处理微服务间的 HTTP 协议上的请求-响应 (Request-Response)。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;Data mappers/ORM&lt;/strong&gt;: 主要的责任是将微服务内的对象持久化。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;微服务与微服务间的协作&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;微服务是能独立发布、独立部署的; 但, 绝不是 “孤立而行”。也就是说, 我们往往是需要多个的微服务来共同提供一具备商业价值的特性; 如图三所示。&lt;/p&gt;    &lt;img alt="" height="934" src="https://www.deva9.com/wp-content/uploads/2018/08/endtoend.png" width="1180"&gt;&lt;/img&gt;图三: 多个微服务共同提供一具备商业价值的特性    &lt;p&gt;对于由多个微服务共同提供一具备商业价值的特性的自动化测试, 我们需考量:&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;多个微服务是由一个以上的团队在负责开发时,如何确保某个团队开发的节奏、自动化的测试, 不会影响到其他团队的微服务的发布、布署? 我们将在 “能独立发布、独立部署微服务的团队阵型” 一文中再作探讨。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;微服务自动化测试的类型&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;单元测试(UNIT TESTING)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;微服务自动化测试中的单元测试, 也是以单个或多个相关的类 (Class) 为测试的单元。&lt;/p&gt;    &lt;p&gt;微服务自动化测试中的单元测试, 也是分成为两类:&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;社交型的单元测试 (Sociable unit testing)&lt;/strong&gt;: 主要是专注测试模块接口的行为是否正确? 而将以类 (Class) 为单元的测试, 当成是黑盒。在微服务自动化测试中, Domain往往是采用社交型的单元测试。因为, 往往我们需要多个Domain 来完成某个微服务 Client 的请求。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;单独型的单元测试 (Solitary unit testing)&lt;/strong&gt;: 主要是专注测试单个类/ 对象和它的依赖间的行为是否正确? 我们往往会将单个类/ 对象的依赖以 Mock 或Stub 的方式来替代。在微服务自动化测试中, 我们往往会将相对独立就能完成自身任务的 Resources, Service layer, Data mappers/ORM, Gateways, HTTP client 采用单独型的单元测试。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;社交型的单元测试, 单独型的单元测试对于保障 “单一个” 微服务内部的质量, 扮演著重要且关键的角色。&lt;/p&gt;    &lt;p&gt;对于保障 “多个” 微服务间的所谓 “系统层级” 的行为正确, 我们将要藉助:&lt;/p&gt;    &lt;p&gt;集成测试 (Integration Testing), 组件测试 (Component Testing), 契约测试 (Contract Testing), 端到端测试 (End-To-End Testing)。&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;集成测试 (Integration Testing)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;在微服务自动化测试中, 所谓的集成测试 (Integration Testing) 主要是测试微服务内的模块能否与外部的微服务或外部的数据库正常的 “通信” ? 而不是在测试外部的微服务或外部的数据库。&lt;/p&gt;    &lt;p&gt;所以, 在微服务自动化测试中的集成测试, 只需测试关键路径上成功/ 异常的场景即可。&lt;/p&gt;    &lt;p&gt;如图四所示, 在微服务内的 Gateways/ HTTP client, Data mappers/ORM 需执行微服务自动化测试中的集成测试。&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;Gateways/ HTTP client &lt;/strong&gt;        &lt;strong&gt;的集成测试&lt;/strong&gt;: 主要是测试微服务内的模块能否与外部的微服务连接, 并且检测通信协议上的问题; 如: 丢失 HTTP 的表头, 不正确的 SSL 处理, request/response 不匹配。关键的一点是: 所有错误处理的场景一定要都能被覆盖测试到。在后面的文章中, 我们也将会探讨运用 Service virtualization 测试关于 timeout 或者外部微服务延迟响应等的场景。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;Data mappers/ORM &lt;/strong&gt;        &lt;strong&gt;的集成测试&lt;/strong&gt;: 主要是测试微服务外部的数据库内的数据表结构是与微服务所期望的数据表结构是一致的。Data mappers/ORM 的集成测试, 对于 NoSQL 的数据库是相当重要、且必要的测试。&lt;/li&gt;&lt;/ul&gt;    &lt;img alt="" height="958" src="https://www.deva9.com/wp-content/uploads/2018/08/intergation_test.png" width="832"&gt;&lt;/img&gt;图四: 集成测试 (Integration Testing)    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;组件测试 (Component Testing)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;微服务自动化测试中的组件测试 (Component Testing), 指的是: 微服务自身的测试; 以微服务作为测试的单元。&lt;/p&gt;    &lt;p&gt;在组件测试 (Component Testing) 中, 测试的粒度是微服务对外的API; 从微服务Client 的视角, 测试微服务对外的API 的行为是否符合预期?&lt;/p&gt;    &lt;p&gt;在组件测试 (Component Testing) 中, 将会以 test doubles (mock/ stub) 的方式, 隔离微服务对外的依赖。&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;如图五所示, 在组件测试 (Component Testing) 中, 为了隔离微服务对外的依赖, Gateways 被配置成去调用 HTTP Client Stub; HTTP Client Stub 将会取代外部的微服务, 而对请求 (Request) 做出响应 (Response)。&lt;/li&gt;      &lt;li&gt;如图五所示, 在组件测试 (Component Testing) 中, 我们会以 In-Memory 数据库; 如: H2; 取代外部数据库。这样的作法, 毫无疑问的, 将大幅的提升组件测试 (Component Testing) 执行的速度。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;在后面的文章中, 我们将会探讨如何以 Arquillian 实现组件测试 (Component Testing) 。&lt;/p&gt;    &lt;img alt="" height="964" src="https://www.deva9.com/wp-content/uploads/2018/08/component-test.png" width="856"&gt;&lt;/img&gt;图五: 组件测试 (Component Testing)    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;契约测试 (Contract Testing)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;微服务是能独立发布、独立部署的; 但, 绝不是 “孤立而行”。也就是说, 我们往往是需要多个的微服务来共同提供一具备商业价值的特性。&lt;/p&gt;    &lt;p&gt;当微服务是扮演提供服务的角色时, 我们便称此微服务是 “Producer”。&lt;/p&gt;    &lt;p&gt;当微服务是扮演使用服务的角色时, 我们便称此微服务是 “Consumer”。&lt;/p&gt;    &lt;p&gt;微服务自动化测试中的契约测试 (Contract Testing), 会在 Producer 微服务与Consumer 微服务之间, 定义一契约 (Contract)。&lt;/p&gt;    &lt;p&gt;契约测试 (Contract Testing) 便会根据 Producer 微服务与 Consumer 微服务之间的契约, 测试 Producer 微服务与 Consumer 微服务之间的交互行为是否正确?  Producer 微服务上代码的修改, 是否会影响到 Consumer 微服务, 而使 Consumer 微服务无法再运行?&lt;/p&gt;    &lt;p&gt;如图六所示:&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;Producer 微服务提供了 Resource: {id, name, age}。&lt;/li&gt;      &lt;li&gt;根据 Contract A, Consumer 微服务 A, 使用了 Producer 微服务, 取得了 Resource: {id, name}。&lt;/li&gt;      &lt;li&gt;根据 Contract B, Consumer 微服务 B, 使用了 Producer 微服务, 取得了 Resource: {id, age}。&lt;/li&gt;      &lt;li&gt;根据 Contract C, Consumer 微服务 C, 使用了 Producer 微服务, 取得了 Resource: {id, name, age}。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;在某一天,&lt;/strong&gt;      &lt;strong&gt;一个新的 Consumer &lt;/strong&gt;      &lt;strong&gt;微服务 Y,&lt;/strong&gt;      &lt;strong&gt;要求 Producer&lt;/strong&gt;      &lt;strong&gt;微服务提供: last name, first name&lt;/strong&gt;      &lt;strong&gt;。&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;Producer 微服务的开发人员, 便将 Producer 微服务中的 name 栏位(属性) 改成 name 类(对象); 封装著 last name, first name。&lt;/li&gt;      &lt;li&gt;经由契约测试 (Contract Testing), Producer 微服务的开发人员将能立马的发现, 他(她) 在 Producer 微服务上的修改, 将会影响到 Consumer A 微服务与 Consumer C 微服务, 而使 Consumer A 微服务与 Consumer C 微服务无法再运行。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;在后面的文章中, 我们将会探讨如何以 Pact 实现契约测试 (Contract Testing)。&lt;/p&gt;    &lt;img alt="" height="956" src="https://www.deva9.com/wp-content/uploads/2018/08/contracttest.png" width="926"&gt;&lt;/img&gt;契约测试 (Contract Testing)    &lt;ul&gt;      &lt;li&gt;        &lt;strong&gt;端到端测试 (End-To-End Testing)&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;微服务自动化测试中的端到端测试 (End-To-End Testing), 主要是要覆盖产品中从前端 GUI 到微服务的测试。&lt;/p&gt;    &lt;p&gt;微服务自动化测试中的端到端测试 (End-To-End Testing) 开发与维护的成本是相当的高的。&lt;/p&gt;    &lt;p&gt;在后面的文章中, 我们将会探讨如何以 Arquillian 实现端到端测试 (End-To-End Testing)。&lt;/p&gt;    &lt;img alt="" height="934" src="https://www.deva9.com/wp-content/uploads/2018/08/endtoend-1.png" width="1180"&gt;&lt;/img&gt;图七: 端到端测试 (End-To-End Testing); 从前端 GUI 到微服务的测试    &lt;p&gt;      &lt;strong&gt;测试金字塔 (Test Pyramid)&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;在了解了各种类型的微服务自动化测试后, 我们就可以将各种类型的微服务自动化测试, 放入测试金字塔 (Test Pyramid) 中。&lt;/p&gt;    &lt;p&gt;如图八所示, 测试金字塔 (Test Pyramid) 可以帮助我们知道如何的去平衡 “测试成本” 与 “测试粗粒度”。&lt;/p&gt;    &lt;p&gt;在测试金字塔 (Test Pyramid)中, 越往上升, 所代表的是: 测试的粗粒度越大, 但测试的成本 (测试执行时间) 就越高 (越长)。&lt;/p&gt;    &lt;p&gt;　所以, 在      &lt;strong&gt;微服务自动化测试的测试策略&lt;/strong&gt;应该是:&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;在测试金字塔 (Test Pyramid) 中, 越往上升的测试类型, 其测试的粗粒度就越大, 而其测试的数量应递减。&lt;/li&gt;      &lt;li&gt;反之, 在测试金字塔 (Test Pyramid) 中, 越往下行的测试类型, 其测试的粗粒度就越小, 而其测试的数量应递增。&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;在微服务自动化测试中,&lt;/strong&gt;        &lt;strong&gt;测试数量最多的测试应该是:&lt;/strong&gt;        &lt;strong&gt;单元测试 (UNIT TESTING)&lt;/strong&gt;        &lt;strong&gt;。&lt;/strong&gt;&lt;/li&gt;      &lt;li&gt;        &lt;strong&gt;在微服务自动化测试中,&lt;/strong&gt;        &lt;strong&gt;测试数量最少的测试应该是:&lt;/strong&gt;        &lt;strong&gt;端到端测试 (End-To-End Testing)&lt;/strong&gt;        &lt;strong&gt;。&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;img alt="" height="556" src="https://www.deva9.com/wp-content/uploads/2018/08/pyramid.png" width="792"&gt;&lt;/img&gt;图八: 测试金字塔(Test Pyramid)    &lt;h3&gt;结论：&lt;/h3&gt;    &lt;p&gt;在分布式架构下的微服务, 要进行自动化测试是件相当复杂的工程。&lt;/p&gt;    &lt;p&gt;软件工程界的巨擘; Martin Fowler; 提供了微服务测试的指引。&lt;/p&gt;    &lt;p&gt;我们可­根据 Martin Fowler 所提供的微服务测试的指引, 制订我们微服务自动化测试的测试策略。我们后续的文章, 也将会根据 Martin Fowler 所提供的微服务测试的指引, 探讨如何运用相关的测试框架、工具, 以实现微服务自动化测试。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;參考資料&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;Testing Strategies in a Microservice Architecture; Martin Fowler&lt;/li&gt;      &lt;li&gt;Microservices Patterns; Chris Richardson&lt;/li&gt;      &lt;li&gt;Testing Java Microservices; Alex Soto Bueno, Andy Gumbrecht, Jason Porter&lt;/li&gt;&lt;/ul&gt;    &lt;div&gt;      &lt;div&gt;        &lt;a href="https://www.addtoany.com/add_to/wechat?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/add_to/sina_weibo?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/add_to/facebook?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/add_to/facebook_messenger?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/add_to/twitter?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Fwww.deva9.com%2Fcloud-streams%2F%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%25b3%25bb%25e5%2588%2597-%25e4%25b8%2580-%25e5%25be%25ae%25e6%259c%258d%25e5%258a%25a1%25e8%2587%25aa%25e5%258a%25a8%25e5%258c%2596%25e6%25b5%258b%25e8%25af%2595%25e7%259a%2584%25e6%25b5%258b%2F&amp;linkname=%E5%BE%AE%E6%9C%8D%E5%8A%A1%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E7%9A%84%E6%B5%8B%E8%AF%95%E7%AD%96%E7%95%A5" rel="nofollow noopener" target="_blank" title=""&gt;&lt;/a&gt;        &lt;a href="https://www.addtoany.com/share"&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61934-%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Sun, 05 Dec 2021 21:19:35 CST</pubDate>
    </item>
    <item>
      <title>契约测试--pact框架使用 - 简书</title>
      <link>https://itindex.net/detail/61933-%E5%A5%91%E7%BA%A6-%E6%B5%8B%E8%AF%95-pact</link>
      <description>&lt;h4&gt;背景&lt;/h4&gt;  &lt;p&gt;最近刚换城市，忙于找工作，趁着等待面试结果的间隙给自己充充电。把之前一直很好奇的“微服务测试”学习了一番，了解了一个新的概念----契约测试。&lt;/p&gt;  &lt;h4&gt;接口的契约&lt;/h4&gt;  &lt;p&gt;对于一个api接口，一般会有接口文档说明。文档中会规定如何调用该接口、如何传参，以及接口会如何响应。其实这个就可以理解为接口的&amp;quot;契约&amp;quot;。前端按照这个“契约”去调用，服务端按照“契约”返回响应的内容。若服务端擅自修改了返回内容结构，就算作违反&amp;quot;契约&amp;quot;，也是造成bug的一个原因。    &lt;br /&gt;扩展到微服务当中，每个服务与服务之间的调用，同样需要遵守这种&amp;quot;契约&amp;quot;，才能保证接口功能的稳定性。&lt;/p&gt;  &lt;h4&gt;什么是契约测试？&lt;/h4&gt;  &lt;p&gt;    &lt;em&gt;微服务架构中，一般分为“提供者”(provider，提供接口的服务) 和“消费者”(consumer，调用接口的服务)&lt;/em&gt;    &lt;br /&gt;接口文档可以称之为对接口契约的具体描述，主要提供给人看。而契约测试，则是将契约具象为代码/工具可识别的形式，比如json、yml、DSL格式。然后借助相关测试工具，根据这份契约，自动测试“消费者/提供者”接口是否正常。    &lt;br /&gt;契约测试一般分两种，一种是消费者驱动，一种是提供者驱动。其中最常用的，是消费者驱动的契约测试（简称 CDC）。即由“消费者”定义出接口“契约”，然后测试“提供者”的接口是否符合契约。&lt;/p&gt;  &lt;h4&gt;契约测试工具使用--PACT&lt;/h4&gt;  &lt;p&gt;契约测试工具貌似有不少，此处介绍一个常用工具--- PACT。    &lt;br /&gt;pact是一个契约测试框架，目前支持java、python、ruby等多种语言。    &lt;br /&gt;pact契约测试分为两步:&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;编写test用例，生成契约文件（不需要启动服务）。&lt;/li&gt;    &lt;li&gt;利用pact-verifier命令和契约文件，验证接口提供者是否正确  (需要启动提供者服务)&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;以下为demo示例：&lt;/p&gt;  &lt;p&gt;1、服务A，提供者&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;import json
from flask import Flask

app = Flask(__name__)

@app.route(&amp;apos;/&amp;apos;)
def get_info():
    info = {
        &amp;quot;name&amp;quot;: &amp;quot;zhangsan&amp;quot;,
        &amp;quot;age&amp;quot;: 20
    }
    return json.dumps(info)


if __name__ == &amp;apos;__main__&amp;apos;:
    app.run(port=8080)&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;2、服务B，消费者。(调用服务A的接口)&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;import json
import requests
from flask import Flask

app = Flask(__name__)

@app.route(&amp;apos;/&amp;apos;)
def show_info():
    res = requests.get(&amp;quot;http://localhost:8080/&amp;quot;).json()
    result = {
        &amp;quot;code&amp;quot;:0,
        &amp;quot;msg&amp;quot;:&amp;quot;ok&amp;quot;,
        &amp;quot;data&amp;quot;: res
    }
    return json.dumps(result)

if __name__ == &amp;apos;__main__&amp;apos;:
    app.run(port=8081)&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;3、测试用例，用于生成契约文件&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;import atexit
import requests
import unittest
from pact.consumer import Consumer
from pact.provider import Provider

# 定义一个pact，消费者是ModuleB，生产者是ModuleA，契约文件存放在pacts文件夹下
pact = Consumer(&amp;apos;ModuleB&amp;apos;).has_pact_with(Provider(&amp;apos;ModuleA&amp;apos;), pact_dir=&amp;apos;./pacts&amp;apos;)
# 启动服务
pact.start_service()
atexit.register(pact.stop_service)

# 测试用例
class UserTesting(unittest.TestCase):

    def test_service(self):
        # 消费者定义的期望结果
        expected = {&amp;quot;name&amp;quot;: &amp;quot;zhangsan&amp;quot;, &amp;quot;age&amp;quot;: 20}
        # 消费者定义的契约的实际内容。包括请求参数、请求方法、请求头、响应值等
        (pact
         .given(&amp;apos;test service.&amp;apos;)
         .upon_receiving(&amp;apos;a request for serviceB&amp;apos;)
         .with_request(&amp;apos;get&amp;apos;, &amp;apos;/&amp;apos;)
         .will_respond_with(200, body=expected))
        # pact自带一个mock服务，端口 1234
        # 用requests向mock接口发送请求，验证mock的结果是否正确
        with pact:
            res = requests.get(&amp;quot;http://localhost:1234&amp;quot;).json()
        self.assertEqual(res, expected)

if __name__ == &amp;quot;__main__&amp;quot;:
    ut = UserTesting()
    ut.test_service()&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;4、实际生成的契约文件    &lt;code&gt;moduleb-modulea.json&lt;/code&gt;&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;{
  &amp;quot;consumer&amp;quot;: {
    &amp;quot;name&amp;quot;: &amp;quot;ModuleB&amp;quot;
  },
  &amp;quot;provider&amp;quot;: {
    &amp;quot;name&amp;quot;: &amp;quot;ModuleA&amp;quot;
  },
  &amp;quot;interactions&amp;quot;: [
    {
      &amp;quot;description&amp;quot;: &amp;quot;a request for serviceB&amp;quot;,
      &amp;quot;providerState&amp;quot;: &amp;quot;test service.&amp;quot;,
      &amp;quot;request&amp;quot;: {
        &amp;quot;method&amp;quot;: &amp;quot;get&amp;quot;,
        &amp;quot;path&amp;quot;: &amp;quot;/&amp;quot;
      },
      &amp;quot;response&amp;quot;: {
        &amp;quot;status&amp;quot;: 200,
        &amp;quot;headers&amp;quot;: {
        },
        &amp;quot;body&amp;quot;: {
          &amp;quot;name&amp;quot;: &amp;quot;zhangsan&amp;quot;,
          &amp;quot;age&amp;quot;: 20
        }
      }
    }
  ],
  &amp;quot;metadata&amp;quot;: {
    &amp;quot;pactSpecification&amp;quot;: {
      &amp;quot;version&amp;quot;: &amp;quot;2.0.0&amp;quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;5、根据契约文件，验证服务A是否正确    &lt;br /&gt;a. 启动服务A(提供者)    &lt;br /&gt;b. 执行    &lt;code&gt;pact-verifier --provider-base-url=http://127.0.0.1:8080 --pact-url=./pacts/moduleb-modulea.json&lt;/code&gt;，即可验证接口是否符合契约&lt;/p&gt;  &lt;h4&gt;契约测试的优点&lt;/h4&gt;  &lt;ul&gt;    &lt;li&gt;在开发接口前定义好契约，消费者和提供者可以并行开发。根据契约可以很容易自测，不必等到联调时才暴露问题&lt;/li&gt;    &lt;li&gt;确保变动的安全性和准确性。只要有变化，契约测试即可第一时间发现&lt;/li&gt;    &lt;li&gt;契约文件可以直观追踪接口的变化&lt;/li&gt;    &lt;li&gt;可以将契约测试集成到CI中&lt;/li&gt;    &lt;li&gt;每次只测试单个服务，更容易定位问题&lt;/li&gt;    &lt;li&gt;来自于模拟服务的可靠响应能够降低测试的不稳定性&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;契约测试与接口测试的区别&lt;/h4&gt;  &lt;p&gt;契约测试与接口测试的原理都是一样的，都是发送请求、验证响应结果。但是接口测试更多的关注业务api的功能、逻辑，而契约测试主要关注接口是否符合“契约”，监控接口的变动。    &lt;br /&gt;契约测试相当于将测试工作前移，在开发阶段就能自测。并且契约测试更加轻量级。&lt;/p&gt;  &lt;h4&gt;参考&lt;/h4&gt;  &lt;p&gt;    &lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Ftesterhome.com%2Ftopics%2F10806" target="_blank"&gt;微服务下的契约测试 (CDC) 解读&lt;/a&gt;    &lt;br /&gt;    &lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Fariman.cn%2F2019%2F05%2F19%2F%25E5%25A5%2591%25E7%25BA%25A6%25E6%25B5%258B%25E8%25AF%2595%25E4%25B9%258B%25E6%25A0%25B8%25E5%25BF%2583%25E8%25A7%25A3%25E6%2583%2591%2F" target="_blank"&gt;契约测试之核心解惑&lt;/a&gt;    &lt;br /&gt;    &lt;a href="https://links.jianshu.com/go?to=https%3A%2F%2Fwww.bookstack.cn%2Fread%2Fpact-zh%2FREADME.md" target="_blank"&gt;Pact中文参考指南&lt;/a&gt;&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61933-%E5%A5%91%E7%BA%A6-%E6%B5%8B%E8%AF%95-pact</guid>
      <pubDate>Sun, 05 Dec 2021 21:07:24 CST</pubDate>
    </item>
    <item>
      <title>接口测试平台演进思考</title>
      <link>https://itindex.net/detail/61924-%E6%8E%A5%E5%8F%A3-%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0-%E6%80%9D%E8%80%83</link>
      <description>&lt;p&gt;很多小伙伴都比较关心如何构建一个接口自动化平台，笔者恰好有从零开始搭建自动化测试平台直到产品商业化的过程经验，可以和大家分享下。由于企业性质的问题，无法分享过多的代码，本文旨在分享个人在构建整个平台变化过程中的思考和总结，给想往这方面发展的小伙伴们一些借鉴，也算是自己的一个阶段性总结。本文主要总结了以下几个问题：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;如何针对团队现状做技术型&lt;/li&gt;
  &lt;li&gt;平台演进过挰中会经历的阶段有哪些，需要分别注意什么&lt;/li&gt;
  &lt;li&gt;区分平台能力和个人能力，不要被表象迷惑&lt;/li&gt;
&lt;/ol&gt;
 &lt;h2&gt;NO.1 关于技术选型的问题&lt;/h2&gt;
 &lt;p&gt;万事开头难，现在世面上关于接口自动化平台的资料，不管是开源的(虽然很多都是Demo级别的），还是商用的，都非常的多。也有一些比较被业内认可的专业化工具，如Pytest，PostMan，Jmeter等等，但具体到自己的团队，应该如何选择一个好的框架做为底层基础呢？（不需要自己再造轮子，也不一定会比别人造的好）？个人认为，至少要从以下几个方面考虑。&lt;/p&gt;
 &lt;h3&gt;1.1 团队的现状是什么？&lt;/h3&gt;
 &lt;p&gt;如果当前团队已经有了部分接口测试的工作在开展，只是没有形成标准化、持续化，那么尽可能就以现有的工具为底层基础，进行开发，培养用户习惯的成本是很高的，别人也不一定乐意，还涉及到迁移成本的问题。如果当前团队还没进行过多的接口测试，那么就可以慎重的进行技术选型&lt;/p&gt;
 &lt;h3&gt;1.2 团队的资源有哪些？&lt;/h3&gt;
 &lt;p&gt;这里指的资源其实就是研发能力，会有几个人一起研发，还是你自己一个人？大家擅长的开发语言是什么？是否有能力做前端页面？是否有时间投入（很多时候并不一定会给工时）。对于开发语言而言，不需要纠结是Java还是Python，擅长哪个就用哪个。能和开发团队保持一致最好，不能也关系不大（如果你真的熟悉了一款语言，那么转换到其它语言上也不是什么难事。笔者从C切换到Java，再到现在的Python，适应过程并不会太长）&lt;/p&gt;
 &lt;h3&gt;1.3 为什么要选它？&lt;/h3&gt;
 &lt;p&gt;在确定完开发语言之后，就可以对应的去做选型了，各语言都有大量的开源框架可以使用（Java的JunitTest，TestNg等，Python的Pytest，HttpRunner等），本质上没有太大的区别，只要你选的框架文档齐全，还在持续更新，问题就不大。没人维护的框架不要选（特别要注意GIT上那些Demo类的框架，很容易误导人）&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;小结：技术问题一定要从团队的实际情况出发来实践，因为做出来的东西，不管是平台还是框架，都是要给到具体的人去落地使用的，所以要尊重你的用户，纯粹show代码能力的事少做（如果真想，去GIT上面提交代码，在公司，还是以解决问题为主）。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在确认完技术选型后，接下来就可以进入研发阶段了。此时千万不要想着要规划一个大而全的平台，而做过多技术设想，应当围绕当下团队最迫切的需求来做，按敏捷的说法，要先交付MVP版本，让团队认可你的平台，真正帮助他们解决问题。后期才会有推动别人使用的空间（这里会涉及到一个问题，那就是个人与平台的关系，很多会觉的如果平台化了，那测试人员的能力如何提升？这个问题放到最后讲，希望你能意识到这个问题，很重要）。当然，如果你有几十人的团队，那另说。&lt;/p&gt;
 &lt;h2&gt;NO.2 第一阶段核心问题：能用&lt;/h2&gt;
 &lt;p&gt;笔者所在的团队当时并不具备进行接口测试的能力，测试人员没有接口测试意识，且无代码能力，需要开展接口测试时，无从下手。分析当时的情况后，制定了本阶段需要解决的痛点。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点1：能够通过页面配置，快速生成接口及其用例。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; 基于HTTP协议（集团内部基本上都是基于此协议交互），在页面上配置相关信息，就可以生成对应的接口，并支持直接调试，避免出现接口不可用的情况。同时，对于HTTP的body内容做了丰富的支持，以便适应不同的参数类型。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/884a6eb35bd444f88d32bfabe68df4a4~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点2：能够支持接口参数传递&lt;/strong&gt;。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案&lt;/strong&gt;：HttrRunner本身就支持参数传递，也支持快速运行用例（框架的好处），所以这个痛点可以很方便的得到解决：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/26ddee60effe4ec483e6edd5d9cec37f~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点3：产出报告对于测试人员更友好些。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; HttpRunner原生的报告比较不友好，所以自己解析原报告数据，做了聚合，让测试人员可以更直观的查看结果，同时可以让测试人员看到参数化或者数据驱动后，发出去的真实请求是什么，方便测试和开发定位问题（遇到报错的接口，如果确认是BUG，直接复制丢给开发，省事省心）。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/faebd548d43f40b98bb704a8b0353ab8~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b71deafdde8a4b94a1d64dc715d8668e~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;小结：在此阶段，其实需要磨合的时间是最长的，团队的磨合、架构的磨合、业务的磨合等等，最重要的是把团队顺利的运转起来。技术上基本没什么大问题，都是基于底层框架原生的能力，做了前端的封装，降低测试人员的使用门槛，让测试人员理解、接受接口测试思想，并指导他们使用平台，设计接口测试用例，让接口测试真正落地并产生效果。&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;NO.3 第二阶段核心问题：好用&lt;/h2&gt;
 &lt;p&gt;在完成第一阶段的内容后，进入第二阶段，这个阶段最核心的思路是推广+优化，目标是让平台好用，  &lt;strong&gt;我们处于并将长期处于这个阶段！！&lt;/strong&gt; 。什么是好用，用户说了算，所以团队花了比较多的时间去落地平台，去分析测试人员的痛点和难点，结合自身的经验和能力，一点点的补充平台功能。简单结总下这个阶段解决的几个典型痛点：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点1：分析接口太麻烦了，需要一个个手动录。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; 通过对接Swagger平台、Fiddler工具等，让测试人员不再纠结接口维护，可以更专注于用例的设计。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6447b1d5d5c6459abd925f1fb245ab4c~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点2：部分接口开发未完成，或者一些外部接口如何处理？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案&lt;/strong&gt;：集成Mock服务，针对已经定义好的接口，可以自定义返回结果，同时生成一个只是域名不同的URL，这样在写测试用例时，想要调用Mock的接口或者真实接口时，，就只需要换个域名即可，其它都不用变，非常灵活。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fc4ef696b34e479a9a4a2aa17fb52092~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点3：当开发的接口发生变化时，测试人员如何第一时间获知呢？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; 提供契约功能，通过算法匹配运行结果与最近一次运行成功的结果做diff,获取接口结构的变更，提醒测试人员。（现在对于Spring框架，有专门的契约测试服务可以使用，笔者当时没有注意到，所以采用了另一种思路）&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b0cd3bdf8a544a3aaba24918de64dbfb~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点4：让测试执行前置，提高测试准入门槛。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; 随着公司CICD的完善，可以让开发人员在合并代码时，自动触发接口测试，验证主流程不受影响。把平台用例对接到公司的流水线上，成为质量门禁的一环，确保新代码不会影响核心功能。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20cb9c0963984c389ceeaea33c2eb3bf~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;痛点5：如何解决业务个性化需求？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：&lt;/strong&gt; 随着服务的团队越来越多，我们需要对应的场景也千奇百怪，如何在标准化的平台上应对一些个性化的需求通过在接口用例中引入前后置函数的功能，让一些个性化的需求，业务团队可以自己编写部分代码去做处理，同时，这部分代码还可以保存成模板，方便复用。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/237a9c4feb484a26a8a21f99ed1e1ab9~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e4b82891278146deab603ff9e0a3fd1e~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;还有一些如：公共登录、环境区分、数据驱动、数据生成、定时任务、文件管理 等等功能点，不再一一列举，主要还是根据团队实际需要来完善功能点，  &lt;strong&gt;最怕的是测试开发人员自嗨，写一些测试人员不太用或者不是痛点的功能，让平台沦落成样子工程&lt;/strong&gt;。一定是要结合测试人员的需求来开发功能点。我们处于并将长期处于这个阶段！！平台使用的技术不是业内最好的，但一定是团队当下的最优解。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;小结：在此阶段，我们获得了很多团队和客户的认可，例如在集团多个BU的落地实践，协助外部客户完成测试价值提升。&lt;/strong&gt;   &lt;strong&gt;平台本身也通过了信通院的DevOps三级认证（先进级），证明了平台的价值，不至于变成样子工程&lt;/strong&gt;   &lt;strong&gt;，这是笔者认为得到的最大的收获和认可。&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;NO.4 第三阶段：未来规划&lt;/h2&gt;
 &lt;p&gt;平台功能需要不断演进，不断满足更多的需求。目前平台也没有停止探索更多的需求，在未来的规划中，我们希望解决以下问题：&lt;/p&gt;
 &lt;p&gt;问题1：测试仓库的搭建，让创建测试数据不再成为难点&lt;/p&gt;
 &lt;p&gt;问题2：接口测试与代码覆盖的对应关系，为精准测试提供数据支撑&lt;/p&gt;
 &lt;p&gt;问题3：逆向用例（混沌测试）的自动化生成&lt;/p&gt;
 &lt;p&gt;问题4：。。。。。。&lt;/p&gt;
 &lt;h2&gt;NO.5 个人与平台&lt;/h2&gt;
 &lt;p&gt;我们回到最初的那个话题，当我们采用平台化来做专项测试时，封装好功能，降低对测试人员的要求，只要通过页面编排就能够执行相关的测试。那么，测试人员如何提升自己呢？这里涉及到的角色有两个：  &lt;strong&gt;个人与组织，这两个角色对平台的诉求是不一样的。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;组织：&lt;/strong&gt; 从组织的角度来说，希望的是有统一规划和管理，同时能够有效降低准入门槛，通过简单的培训，让更多的测试人员能够开展专项测试，所以更需要平台化的管理方式，能够提供标准化的、可视化的、操作方便的服务 。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;个人：&lt;/strong&gt; 当你进入到一个团队中，有现成的平台给你使用时，一定不能满足于使用平台，而是应该抓住机会去了解平台的具体实现，甚至参与到平台的研发过程中，把别人的开发设计和解决问题的思路变成自己的心得体会。如果只会依赖公司平台开展专项测试，那是平台的能力，而不是个人的能力。这也是为什么很多面试官不太喜欢大厂出来的同学，因为离开了平台，个人的能力有可能出现断崖式的下跌，切记（其实很多岗位都会有类似的问题，要学会区分哪些是自己的能力，哪些是平台给你的加成）。&lt;/p&gt;
 &lt;p&gt;最后，感谢团队所有成员的付出和努力。团队的能力远大于个人的能力，如果能够有一群志同道合的朋友一起为同一个目标而努力，那是件很幸福的事。&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://mp.weixin.qq.com/s/UNtRSnaSszpZx5v-hcUhJg"&gt;原文链接&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61924-%E6%8E%A5%E5%8F%A3-%E6%B5%8B%E8%AF%95%E5%B9%B3%E5%8F%B0-%E6%80%9D%E8%80%83</guid>
      <pubDate>Mon, 29 Nov 2021 08:59:44 CST</pubDate>
    </item>
    <item>
      <title>timescaleDB双机热备流复制与测试</title>
      <link>https://itindex.net/detail/61770-timescaledb-%E5%A4%8D%E5%88%B6-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;h2&gt;背景&lt;/h2&gt;



 &lt;p&gt;最近有项目要用到热备功能，timescaledb只能兼容pg的流复制，不能兼容其他的复制策略，所以这里我们采用pg的流复制功能镜像部署，并进行了一些测试&lt;/p&gt;



 &lt;hr&gt;&lt;/hr&gt;



 &lt;h2&gt;timescaleDB安装（两台机器都安装）&lt;/h2&gt;



 &lt;p&gt;1.添加postgresql源&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;echo &amp;quot;deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -c -s)-pgdg main&amp;quot; | sudo tee /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;2.安装timescaledb&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo add-apt-repository ppa:timescale/timescaledb-ppa
sudo apt-get update
sudo apt install timescaledb-1.7.5-postgresql-11&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;3.安装postgis&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo apt install postgresql-11-postgis-2.5

&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;4.进行数据库调优&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo timescaledb-tune -yes
&lt;/code&gt;&lt;/pre&gt;



 &lt;hr&gt;&lt;/hr&gt;



 &lt;h2&gt;timescaledb流复制配置(主节点配置)&lt;/h2&gt;



 &lt;p&gt;修改配置文件sudo nano /etc/postgresql/11/main/postgresql.conf&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt; 
# postgresql.conf
wal_level = replica
max_wal_senders = 16                      # 最多多少各流复制链接
wal_keep_segments = 256                   # 流复制保留最多的xlog数
wal_sender_timeout = 60s                  # 流复制主机发送数据超时时间
max_connections = 1000                    # 从库的max_connections必须大于主库的
 
full_page_writes = on                     # 使用pg_rewind命令同步数据库要用
wal_log_hints = on                        # 使用pg_rewind命令同步数据库要用
hot_standby = on                          # 使用pg_rewind命令同步数据库要用
 
listen_addresses = &amp;apos;*&amp;apos;                    # 修改监听
archive_mode = on                         # 开启归档模式
archive_command = &amp;apos;arch.sh %f %p&amp;apos;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;创建replica用户，密码replica123&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo -u postgres psql
CREATE ROLE replica login replication encrypted password &amp;apos;replica123&amp;apos;;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;在/var/lib/postgresql/11/main创建arch.sh,用于定时删除超过7天的归档文件内容如下&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;
PGDATA=/var/lib/postgresql/11/main
test ! -f $PGDATA/arch/$1 &amp;amp;&amp;amp; cp --preserve=timestamps $2 $PGDATA/arch/$1 ; find /arch/ -type f -mtime +7 -exec rm -f {} \;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;在/var/lib/postgresql/11/main创建archive目录，赋权给archive和arch.sh&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;mkdir /var/lib/postgresql/11/main/archive
chown -R postgres:postgres arch*&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;确认归档功能开启&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;root@database-master:/var/lib/postgresql/11/main# ps -ef|grep archiver
postgres 29921 29916  0 08:36 ?        00:00:00 postgres: 11/main: archiver  
root     29953 29477  0 08:36 pts/0    00:00:00 grep --color=auto archiver
&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;配置sudo nano /etc/postgresql/11/main/pg_hba.conf   &lt;/p&gt;



 &lt;p&gt;备注：192.168.0.31 是备节点的IP&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;# 在配置文件末尾添加
pg_hba.conf
host    all             all             0.0.0.0/0               md5
host    replication     replica         192.168.0.31/32         md5
&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;重启数据库&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;



 &lt;hr&gt;&lt;/hr&gt;



 &lt;h2&gt; timescaledb流复制配置(从节点配置) &lt;/h2&gt;



 &lt;p&gt; 配置sudo nano /etc/postgresql/11/main/pg_hba.conf    &lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;# 在配置文件末尾添加
pg_hba.conf
host    all             all             0.0.0.0/0               md5
host    replication     replica         192.168.0.31/32         md5&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;删除数据目录&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo rm -rf /var/lib/postgresql/11/main
sudo pg_basebackup -D /var/lib/postgresql/11/main -Fp -Xs -v -P -h 192.168.0.30 -U replica -W&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;复制之后，注意设置main目录的权限为postgres&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;
sudo chown -R postgres:postgres /var/lib/postgresql/11/main
sudo systemctl restart postgresql

&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;在 /var/lib/postgresql/11/main 下添加recovery.conf文件，内容如下：&lt;/p&gt;



 &lt;p&gt;注意192.168.0.30是主节点的IP&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;standby_mode = on
primary_conninfo = &amp;apos;host=192.168.0.30 port=5432 user=replica password=replica123&amp;apos;
recovery_target_timeline = &amp;apos;latest&amp;apos;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt; 修改配置文件sudo nano /etc/postgresql/11/main/postgresql.conf &lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;# postgresql.conf
max_connections = 10000                    # 从库的max_connections必须大于主库的
max_standby_streaming_delay = 30s
wal_receiver_status_interval = 10s
hot_standby_feedback = on

full_page_writes = on                     # 使用pg_rewind命令同步数据库要用
wal_log_hints = on                        # 使用pg_rewind命令同步数据库要用
hot_standby = on                          # 使用pg_rewind命令同步数据库要用

listen_addresses = &amp;apos;*&amp;apos;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt; 重启数据库 &lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;sudo systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;



 &lt;hr&gt;&lt;/hr&gt;



 &lt;h2&gt;场景测试&lt;/h2&gt;



 &lt;p&gt;功能说明：&lt;/p&gt;



 &lt;p&gt;主库可读可写，备库只可读。备库的数据会通过异步的方式实时从主库同步。&lt;/p&gt;



 &lt;p&gt;备库关闭，不影响主库的使用，备库重启之后，能自动从主库同步这段时间缺失的数据。&lt;/p&gt;



 &lt;p&gt;主库关闭，备库不会自动切换成主库，等主库开机之后，主备功能自动恢复。&lt;/p&gt;



 &lt;h3&gt;场景1：确认foreign table可兼容双击热备&lt;/h3&gt;



 &lt;p&gt;步骤1：在master机器上创建两个库，并对其中的一些表之间建立起foreign table。&lt;/p&gt;



 &lt;p&gt;步骤2：检查slver机器上也自动生成了两个库和对应的外表&lt;/p&gt;



 &lt;p&gt;步骤3：在测试环境上安装应用程序，进行sql insert操作&lt;/p&gt;



 &lt;p&gt;步骤4：检查master和slaver上的4张表数据是否一致&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;结论：foreign table功能能兼容双击热备，不会丢数据&lt;/strong&gt;&lt;/p&gt;



 &lt;h3&gt;场景2：在小数据量时，如果备库关闭，过一段时间再重启，会有什么后果？&lt;/h3&gt;



 &lt;p&gt;步骤1：在完成场景1的情况下，修改插入频率，提高到每间隔1秒500条数据&lt;/p&gt;



 &lt;p&gt;步骤2：持续了2分钟之后，突然在slaver机器上运行systemctl stop postgresql&lt;/p&gt;



 &lt;p&gt;步骤3：master机器继续工作，master上的数据持续增加&lt;/p&gt;



 &lt;p&gt;步骤4：1小时后，在slaver机器上运行systemctl start postgresql 启动数据库&lt;/p&gt;



 &lt;p&gt;步骤5：检查slaver机器上缺少的数据是否会补充回来&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;结论：slaver机器关闭再重启，这段时间缺失的数据会补充回来&lt;/strong&gt;&lt;/p&gt;



 &lt;h3&gt;场景3：在大数据量时，如果备库关闭，过一段时间再重启，会有什么后果？&lt;/h3&gt;



 &lt;p&gt;步骤1：在完成场景1的情况下，修改插入频率，提高到每间隔1秒500000个条数据&lt;/p&gt;



 &lt;p&gt;步骤2：持续了2分钟之后，突然在slaver机器上运行systemctl stop postgresql&lt;/p&gt;



 &lt;p&gt;步骤3：master机器继续工作，master上的数据持续增加&lt;/p&gt;



 &lt;p&gt;步骤4：1小时候在slaver机器上运行systemctl start postgresql 启动数据库&lt;/p&gt;



 &lt;p&gt;步骤5：检查slaver机器上缺少的数据是否会补充回来&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;结论：slaver机器关闭再重启，这段时间缺失的数据会通过wal日志补充回来，补充速度看备机的硬盘io&lt;/strong&gt;&lt;/p&gt;



 &lt;h3&gt;场景4：在大数据量时，如果主库关闭，过一段时间再重启，主备能自动恢复连接吗？&lt;/h3&gt;



 &lt;p&gt;步骤1：在完成场景1的情况下，修改遥测频率，提高到每间隔30秒50000个遥测数据&lt;/p&gt;



 &lt;p&gt;步骤2：持续了一段时间之后，突然在master机器上运行systemctl stop postgresql&lt;/p&gt;



 &lt;p&gt;步骤3：此时应用程序出错，遥测无法上传&lt;/p&gt;



 &lt;p&gt;步骤4：过60分钟之后，在master机器上运行systemctl start postgresql 启动数据库&lt;/p&gt;



 &lt;p&gt;步骤5：检查连接，和主备数据库，删除和增加数据，检查是否自动同步&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;select client_addr,sync_state from pg_stat_replication;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;  &lt;strong&gt;结论：master机器关闭再重启，能够自动与备库建立连接，并且不影响后续使用&lt;/strong&gt;&lt;/p&gt;



 &lt;h3&gt;场景5：主库关闭，备库切换成主库，原主库能改成备库吗，程序能继续使用吗？&lt;/h3&gt;



 &lt;p&gt;步骤1：关闭主库systemctl stop postgresql&lt;/p&gt;



 &lt;p&gt;步骤2：在备库上linux用户切换到postgres，然后添加pg_ctl到环境变量&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;$ cd ~
$ vim .profile
PATH=$PATH:/usr/lib/postgresql/11/bin
export PATH
$ . ~/.profile&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;步骤3：在备库上输入pg_ctl promote -D /var/lib/postgresql/11/main。此时会发现/var/lib/postgresql/11/main下的recovery.done变成了recovery.conf&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;postgres@database-slaver:/home/sfere$ pg_ctl promote -D /var/lib/postgresql/11/main
waiting for server to promote.... done
server promoted&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;步骤4：在老的主库上，使用postgres用户登录，使用pg_rewind同步数据&lt;/p&gt;



 &lt;pre&gt;  &lt;code&gt;pg_rewind -D /var/lib/postgresql/11/main --source-server=&amp;apos;hostaddr=192.168.0.31 port=5432 user=postgres password=postgres&amp;apos;&lt;/code&gt;&lt;/pre&gt;



 &lt;p&gt;步骤5：修改应用程序的数据库连接配置到新的主库，继续进行sql insert操作&lt;/p&gt;



 &lt;p&gt;   &lt;strong&gt;结论：主库备库切换完成之后，修改应用程序数据库连接池配置，不影响后续使用&lt;/strong&gt; &lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据库相关</category>
      <guid isPermaLink="true">https://itindex.net/detail/61770-timescaledb-%E5%A4%8D%E5%88%B6-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Wed, 08 Sep 2021 13:10:13 CST</pubDate>
    </item>
    <item>
      <title>自动化接口测试实践经验</title>
      <link>https://itindex.net/detail/61671-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%8E%A5%E5%8F%A3-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;p&gt;    &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;作者：faithchen，腾讯 PCG 测试开发工程师    &lt;br /&gt;&lt;/p&gt;  &lt;h3&gt;一、背景&lt;/h3&gt;  &lt;p&gt;自动化测试对于我们提升研发效能、CI/CD(持续集成/持续交付)是不可或缺的部分。在后台自动化测试中，接口测试尤为重要，它能够保证被测后台服务的质量，以及接口逻辑的正确性等，帮助我们快速测试功能、提高测试覆盖率、把控质量风险等。&lt;/p&gt;  &lt;h4&gt;1.1 后台接口测试&lt;/h4&gt;  &lt;p&gt;接口测试是功能测试的一种，是测试系统组件间接口的一种测试，重点在于检验对于服务接口的数据交换的正确性，一般全部依赖真实链路，测试时需要启动被测服务。如下图是某个Server A的接口测试：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;然而，写过接口测试的同学可能都会被复杂的数据构造以及繁杂的断言所困扰，可能需要耗费大半天才能写完一个接口测试用例，浪费了许多宝贵的时间！基于以上考虑，为了提高编写接口测试用例的效率，我们希望能够自动化地协助开发或测试人员完成这些耗时耗力的事情，为此而产生的想法是通过流量的录制，再通过录制的流量自动化生成接口测试用例。基于上述想法，我们提供了从    &lt;strong&gt;录制流量&lt;/strong&gt;到    &lt;strong&gt;生成接口测试用例&lt;/strong&gt;完整链路的工具，辅助开发同学快速完成编写接口测试用例。简单来说，只需要以下三个步骤便可以接入：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;接下来，我们通过**“流量录制-&amp;gt;生成用例”    &lt;strong&gt;的流程来进行阐述我们的实现方法和接入方式。我们会说明通用的平台如stke、sumeru服务的接入流程，由于123平台对goreplay等有较好的支持，除了通用流程外会以123平台为例进行说明。最后会说明&lt;/strong&gt;流量频次筛选方案**帮助大家理解我们的流程。&lt;/p&gt;  &lt;h3&gt;二、流量录制&lt;/h3&gt;  &lt;h4&gt;2.1 流量录制简介&lt;/h4&gt;  &lt;h5&gt;2.1.1 什么是流量录制&lt;/h5&gt;  &lt;p&gt;我们听得比较多的是“录制与回放”，但目前只需要用到录制功能，后续可能会把回放功能也加上。顾名思义，流量录制就是指将我们期望的接口数据的包括response、request、协议等等存储起来的操作，可以是正式环境或者是测试环境的数据，开启了录制功能后，只要对应环境的服务有流量，便可以将其捕获存储起来，实现此功能的方式有多种。&lt;/p&gt;  &lt;p&gt;通过对多个方案的对比后，我们决定采用goreplay的方式，其开源代码是    &lt;a href="https://github.com/buger/goreplay"&gt;goreplay&lt;/a&gt;，基于goreplay 1.2.0版本改造后已经支持trpc, http, gofree, wup, videopacket, 后面会陆陆续续支持其他协议，采用goreplay的主要优势是它可以分析和记录服务的流量但不会影响服务，并且接入流程相对简单。&lt;/p&gt;  &lt;p&gt;goreplay方案其最大好处是与业务代码解耦，不需要入侵代码。&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;在传输层的goreplay录制方式为：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;h5&gt;2.1.2 为什么要进行流量录制&lt;/h5&gt;  &lt;p&gt;接口测试是在真实运行的服务中测试，若想自动化生成用例，理想的情况下，便是要自动化生成真实数据的接口测试用例，那么首先需要的就是    &lt;strong&gt;真实的接口数据&lt;/strong&gt;，数据从何而来呢？自然而然的，我们便会想到到线上去录制（无论是测试环境或者正式环境的）。&lt;/p&gt;  &lt;p&gt;下面为「通用流程」讲述如何为服务接入goreplay流量录制流程。&lt;/p&gt;  &lt;h4&gt;2.2 「通用流程」服务流量录制&lt;/h4&gt;  &lt;h5&gt;2.2.1 「通用流程」上传pb文件至协议中台用于解析二进制流量&lt;/h5&gt;  &lt;p&gt;首先我们需要一个能够管理协议的系统，它能够对协议文件（如pb文件等）进行管理，这样当协议有更新时，依然能够对新的协议生成的流量进行解析。&lt;/p&gt;  &lt;p&gt;能够将goreplay录制的二进制数据进行解析：&lt;/p&gt;  &lt;p&gt;    &lt;img&gt;&lt;/img&gt;&lt;/p&gt;只需要将依赖的proto文件进行打包（注意依赖多个proto文件时保持相对目录正确）并上传到协议中台即可，并且生成对应此版本pb的版本号，用于我们后续生成goreplay命令。  &lt;h5&gt;2.2.2 「通用流程」生成并执行goReplay命令&lt;/h5&gt;  &lt;p&gt;在上述协议中台获取了版本号之后，我们已知后台服务的ip和端口，已经最新的协议版本。将这些填入我们的goreplay命令中，如下图所示：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;将上述命令在对应的容器中执行。&lt;/p&gt;  &lt;p&gt;部分参数说明：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;--input-raw-logreplay// 这个其实是rawInput插件的参数, 开启这个参数后, 必须ip和port都要填      &lt;br /&gt;--output-logreplay-cache-size 200// LogReplayOutPut插件 需要的缓存大小, 可以不设置, 有默认值      &lt;br /&gt;--output-logreplay-track-response// 跟--input-raw-track-response含义一样      &lt;br /&gt;--input-raw-protocol // 必填需要解析的协议      &lt;br /&gt;--output-logreplay-protocol-service-name &amp;quot;app.server&amp;quot; // 协议文件对的服务名      &lt;br /&gt;--output-logreplay-commitid &amp;quot;1.0.1&amp;quot; // 必填,如果是http协议, 可以自定义      &lt;br /&gt;--output-logreplay-qps-limit 100// 限制录制的qps, 非必填, 有默认值      &lt;br /&gt;--output-logreplay-env formal // 选填，默认formal，需要注册到logreplay测试环境可填test      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在容器中执行    &lt;code&gt;ps -ef|grep goreplay&lt;/code&gt;看到如图进程便表示成功了：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;h5&gt;2.2.3「通用流程」查看录制流量&lt;/h5&gt;  &lt;p&gt;当服务在对应环境有流量的话，很快我们就可以查看到通过goreplay录制到并解析成功的流量了。&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;里面的request和respond等数据都一目了然（此处是demo所以数据很少，真实环境中，可能有非常多的参数及数据），根据这些录制的数据，我们下来就可以自动生成接口测试用例了。&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;h3&gt;三、生成用例&lt;/h3&gt;  &lt;p&gt;当录制好流量后，我们会有一个筛选机制来过滤流量，不过这个操作对于用户是无感知的。对于使用方来说，接下来就能够    &lt;strong&gt;自动化&lt;/strong&gt;生成流量了。&lt;/p&gt;  &lt;h4&gt;3.1 px-cmdline工具简介&lt;/h4&gt;  &lt;p&gt;我们提供了px-cmdline工具，这是一个命令行工具，它可以将goreplay录制的流量，通过proto文件，自动获取相关依赖后生成符合代码规范的接口测试文件。&lt;/p&gt;  &lt;h4&gt;3.2 px-cmdline使用&lt;/h4&gt;  &lt;p&gt;使用方式也很简单，命令行格式为：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;px-cmdline generate -p &amp;lt;proto gomod&amp;gt; [-f] [--env test]      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;常用参数介绍:&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;-p 指定proto依赖路径，从go.mod中拷贝&lt;/li&gt;    &lt;li&gt;-f 是否强制更新，开启后，会替换掉即将生成的同名接口测试文件&lt;/li&gt;    &lt;li&gt;--env 指定拉取流量的环境变量，默认test环境&lt;/li&gt;    &lt;li&gt;--dest 指定生成的目录，默认tests文件夹&lt;/li&gt;    &lt;li&gt;-j 指定是否切换json文件模式。默认使用json文件独立模式；开启后，用例写入到*_test.go里&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;示例：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;px-cmdline generate -p git.code.oa.com/xxxxprotocol/hello/world      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;运行后，如果未发生异常，会在当前目录下生成tests子目录，根据每个RPC接口生成独立的__test.go文件，以及一个唯一的suite_bvt_base_test.go用于启动接口测试。testdata目录存放json格式的用例数据。如图：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;当服务有多个运行节点的时候，还支持选择运行节点，以便用于本地调试接口用例时请求该目标。建议优先选择test环境或者开发者的特性环境&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;[Info] 请选择本地测试目标服务：可只选择环境，或指定节点      &lt;br /&gt;[Info] (1) 测试环境 test --------      &lt;br /&gt;[Info] (└ 11 ) 9.2.3.4:16916, Development, 权重100, 健康      &lt;br /&gt;[Info] (2) 特性环境 sdfdfe --------      &lt;br /&gt;[Info] (└ 21 ) 11.1.2.3:11389, Development, 权重100, 健康      &lt;br /&gt;[Info] (3) 特性环境 dfadfb --------      &lt;br /&gt;[Info] (└ 31 ) 9.3.4.5:11042, Development, 权重100, 健康      &lt;br /&gt;[Info] (4) 特性环境 c9d193e8 --------      &lt;br /&gt;[Info] (└ 41 ) 11.3.4.5:11222, Development, 权重100, 健康      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;为了符合代码规范，用例数据目前以json数据的形式进行存储，展示其中一条数据如下：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;{      &lt;br /&gt;&amp;quot;request&amp;quot;: &amp;quot;{\&amp;quot;pp\&amp;quot;:[\&amp;quot;1dfa\&amp;quot;]}&amp;quot;,      &lt;br /&gt;&amp;quot;response&amp;quot;: &amp;quot;{\&amp;quot;sdf\&amp;quot;:{\&amp;quot;TPDKH\&amp;quot;:{\&amp;quot;basic\&amp;quot;:{},\&amp;quot;cosume\&amp;quot;:{},\&amp;quot;data\&amp;quot;:{\&amp;quot;type\&amp;quot;:\&amp;quot;NotSettle\&amp;quot;,\&amp;quot;pll\&amp;quot;:[733],\&amp;quot;tpl\&amp;quot;:\&amp;quot;UG\&amp;quot;}&amp;quot;,      &lt;br /&gt;&amp;quot;trace_id&amp;quot;: &amp;quot;394729374872934&amp;quot;      &lt;br /&gt; },      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;如此一来就成功生成了接口测试用例了，当我们本地调试OK后，就可以将其mr进代码主干，在日常流水线运行时都能对接口进行自动化测试。&lt;/p&gt;  &lt;p&gt;当然了，如果有同学喜欢用更直观的IDE工具，我们也提供了IDE插件供大家使用。&lt;/p&gt;  &lt;h4&gt;3.3 px-cmdline 工具（IDE版） Jetbrains 插件使用&lt;/h4&gt;  &lt;p&gt;如果用户希望通过IDE插件，可以通过如下操作&lt;/p&gt;  &lt;p&gt;第一步：我们有相关的插件供安装&lt;/p&gt;  &lt;p&gt;    &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;br /&gt;  &lt;p&gt;弹出窗里面选择刚才下载的ZIP包，然后确定；&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第二步：安装完成后重启IDE，启用插件&lt;/strong&gt;通过插件简单右键选择来生成接口测试文件可以达到与命令行工具同样的效果：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;img  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;然后便可以顺利生成接口和用例在特定的文件夹中，用于接口测试执行。&lt;/p&gt;  &lt;h3&gt;四、流量频次筛选策略&lt;/h3&gt;  &lt;p&gt;本章节补充说明，当我们数据量较大的时候，如何进行筛选与分析，保留下相对有价值的流量用于生成接口测试用例的。&lt;/p&gt;  &lt;h4&gt;4.1 流量筛选背景&lt;/h4&gt;  &lt;p&gt;本章节内容讲述我们的流量筛选策略，这部分内容对于使用用户是无感知的，但是有助于对我们工具的完整链路有更好的理解。&lt;/p&gt;  &lt;p&gt;当我们为服务接入goreplay后，在服务上有成千上万的流量录制进来，有时候甚至更多，那么怎么知道哪些流量是重复的，哪些流量值得记录下来用于生成用例呢？&lt;/p&gt;  &lt;p&gt;由于正式线上环境流量较大，可能存在很多流量数据的重复率和相似度较高的情况，对此，我们不会简单的将所有流量都直接存储到数据后用于后续生成接口测试（毕竟我们也不可能用成千上万个接口用例来测试一条数据），而是会通过一定的策略进行    &lt;strong&gt;频次筛选&lt;/strong&gt;，符合我们相似度区分要求的流量我们才会存储到我们的数据库中。&lt;/p&gt;  &lt;p&gt;这样做的好处有：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;防止某些服务流量过大，存储数据过大。&lt;/li&gt;    &lt;li&gt;剔除重复流量，对于相似度高的流量不重复存储，同时保证了接口测试的数据多样化。&lt;/li&gt;&lt;/ol&gt;  &lt;h4&gt;4.2 相似度对比简述&lt;/h4&gt;  &lt;h5&gt;4.2.1 相似度比较总体流程&lt;/h5&gt;  &lt;p&gt;如下图，例子中有两个流量的response，我们是如何对response或者request的数据进行相似度计算的呢，大致流程为：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;p&gt;取其body部分，舍弃header&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;计算一级key相似度，一级key的相似度我们较严格，一般情况下，如果一级key不一样那么则可以认为这两个body之间的差异是较大的，如下图左侧response的一级key有        &lt;code&gt;Msg&lt;/code&gt;、        &lt;code&gt;ProcId&lt;/code&gt;、        &lt;code&gt;TaskOutput&lt;/code&gt;，右侧response的一级key有        &lt;code&gt;Msg&lt;/code&gt;、        &lt;code&gt;ProcId&lt;/code&gt;、        &lt;code&gt;Code&lt;/code&gt;，显然，我们会认为这两个相似度是较低的，应该是差异较大的流量。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;假如一级key完全一样的话，显然要进一步递归比较，但是层级越高我们会设置影响因子越小，对此完成相似度的递归计算。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;img&gt;&lt;/img&gt;  &lt;h5&gt;4.2.2 字符串相似度&lt;/h5&gt;  &lt;p&gt;一般计算到二级key以后，我们便要开始关心字符串的相似度了，此处我们使用的算法是    &lt;a href="https://zh.wikipedia.org/wiki/%E8%90%8A%E6%96%87%E6%96%AF%E5%9D%A6%E8%B7%9D%E9%9B%A2"&gt;Levenshtein距离&lt;/a&gt;，定义如下：&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;简单说来，就是计算一个字符串需要经过多少次编辑才能变成另一个字符串，结合它们的字符长度和编辑次数，我们便可以计算两者之间的相似度。&lt;/p&gt;  &lt;p&gt;例如，将“kitten”一字转成“sitting”的莱文斯坦距离为3：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;      &lt;strong&gt;k&lt;/strong&gt;itten →      &lt;strong&gt;s&lt;/strong&gt;itten （k→s）&lt;/li&gt;    &lt;li&gt;sitt      &lt;strong&gt;e&lt;/strong&gt;n → sitt      &lt;strong&gt;i&lt;/strong&gt;n （e→i）&lt;/li&gt;    &lt;li&gt;sittin → sittin      &lt;strong&gt;g&lt;/strong&gt;（插入g）&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;当获得字符串相似度之后，进而可以计算出json的相似度即response和request的相似度，再结合多级key的影响因子策略进而计算出两条流量的相似度。&lt;/p&gt;  &lt;h4&gt;4.3 筛选策略&lt;/h4&gt;  &lt;p&gt;获得相似度之后，我们还会通过多层策略来筛选。&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;p&gt;设置response和request的比重，因为通常response更好地反应接口的差异，设置比重可以更好的衡量流量间的相似度，利于后续筛选。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;设置多层动态阈值，随着数据量的增加，动态调节能够存入的相似度阈值，以便更好地控制记录的流量数目，保证记录下来的流量数在后期是收敛的，不会无限增加。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;记录流量重复次数，可以用于后续生成用例，我们认为重复更多次数的流量其重要性越大。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;通过多重策略，来保证我们能够筛选出“优质”的流量用于后续接口测试。&lt;/p&gt;  &lt;h3&gt;五、总结及其他&lt;/h3&gt;  &lt;p&gt;目前本方案已经为多个服务（包括微视、企鹅号等）生成多条接口测试用例，已经超过1000条合入到服务中在日常mr中进行接口测试。&lt;/p&gt;  &lt;img&gt;&lt;/img&gt;  &lt;img&gt;&lt;/img&gt;  &lt;p&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;近期热文：&lt;/p&gt;  &lt;p&gt;    &lt;a href="https://mp.weixin.qq.com/s?__biz=MjM5ODYwMjI2MA==&amp;mid=2649762095&amp;idx=1&amp;sn=3129218cee0363f1d32e0f262617d7c5&amp;chksm=beccba5489bb33420d1f9dad2cc69a66a0efe43f7e8bf005c1160485b74df1c16ae7eef95056&amp;scene=21#wechat_redirect" target="_blank"&gt;5000 万行以上大型代码仓库工程实践&lt;/a&gt;    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;    &lt;a href="https://mp.weixin.qq.com/s?__biz=MjM5ODYwMjI2MA==&amp;mid=2649761883&amp;idx=1&amp;sn=11c28943b96279317c8de1b59f6ead30&amp;chksm=beccbb2089bb32362014b6a7639b5f5cc0541c8961f30d24ba0e60d7722653ed612deaf111ac&amp;scene=21#wechat_redirect" target="_blank"&gt;带你快速了解 Docker 和 Kubernetes&lt;/a&gt;&lt;/p&gt;  &lt;br /&gt;                                                           &lt;strong&gt;腾讯极客挑战赛来啦！&lt;/strong&gt;  &lt;br /&gt;  &lt;br /&gt;  &lt;p&gt;    &lt;img&gt;&lt;/img&gt;&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61671-%E8%87%AA%E5%8A%A8%E5%8C%96-%E6%8E%A5%E5%8F%A3-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Mon, 09 Aug 2021 17:52:37 CST</pubDate>
    </item>
    <item>
      <title>[翻译]使用Spring Boot进行单元测试</title>
      <link>https://itindex.net/detail/61529-%E7%BF%BB%E8%AF%91-spring-boot</link>
      <description>&lt;p&gt;原文地址：  &lt;a href="https://reflectoring.io/unit-testing-spring-boot/" rel="nofollow noreferrer"&gt;https://reflectoring.io/unit-...&lt;/a&gt;&lt;/p&gt; &lt;p&gt;编写好的单元测试可以被看成一个很难掌握的艺术。但好消息是支持单元测试的机制很容易学习。&lt;/p&gt; &lt;p&gt;本文给你提供在Spring Boot 应用程序中编写好的单元测试的机制，并且深入技术细节。&lt;/p&gt; &lt;p&gt;我们将带你学习如何以可测试的方式创建Spring Bean实例，然后讨论如何使用  &lt;code&gt;Mockito&lt;/code&gt;和  &lt;code&gt;AssertJ&lt;/code&gt;，这两个包在Spring Boot中都为了测试默认引用了。&lt;/p&gt; &lt;p&gt;本文只讨论单元测试。至于集成测试，测试web层和测试持久层将会在接下来的系列文章中进行讨论。&lt;/p&gt; &lt;h2&gt;代码示例&lt;/h2&gt; &lt;p&gt;本文附带的代码示例地址：  &lt;a href="https://github.com/thombergs/code-examples/tree/master/spring-boot/spring-boot-testing" rel="nofollow noreferrer"&gt;spring-boot-testing&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;使用 Spring Boot 进行测试系列文章&lt;/h2&gt; &lt;p&gt;这个教程是一个系列：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;使用 Spring Boot 进行单元测试（本文）&lt;/li&gt;  &lt;li&gt;使用 Spring Boot 和 @WebMvcTest 测试SpringMVC controller层&lt;/li&gt;  &lt;li&gt;使用 Spring Boot 和 @DataJpaTest 测试JPA持久层查询&lt;/li&gt;  &lt;li&gt;通过 @SpringBootTest 进行集成测试&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;如果你喜欢看视频教程，可以看看  &lt;code&gt;Philip&lt;/code&gt;的课程：  &lt;a href="https://transactions.sendowl.com/stores/13745/194393" rel="nofollow noreferrer"&gt;测试Spring Boot应用程序课程&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;依赖项&lt;/h2&gt; &lt;p&gt;本文中，为了进行单元测试，我们会使用  &lt;code&gt;JUnit Jupiter（Junit 5）&lt;/code&gt;，  &lt;code&gt;Mockito&lt;/code&gt;和  &lt;code&gt;AssertJ&lt;/code&gt;。此外，我们会引用  &lt;code&gt;Lombok&lt;/code&gt;来减少一些模板代码：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;dependencies{
  compileOnly(&amp;apos;org.projectlombok:lombok&amp;apos;)
  testCompile(&amp;apos;org.springframework.boot:spring-boot-starter-test&amp;apos;)
  testCompile &amp;apos;org.junit.jupiter:junit-jupiter-engine:5.2.0&amp;apos;
  testCompile(&amp;apos;org.mockito:mockito-junit-jupiter:2.23.0&amp;apos;)
}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;code&gt;Mockito&lt;/code&gt;和  &lt;code&gt;AssertJ&lt;/code&gt;会在  &lt;code&gt;spring-boot-test&lt;/code&gt;依赖中自动引用，但是我们需要自己引用  &lt;code&gt;Lombok&lt;/code&gt;。&lt;/p&gt; &lt;h2&gt;不要在单元测试中使用Spring&lt;/h2&gt; &lt;p&gt;如果你以前使用  &lt;code&gt;Spring&lt;/code&gt;或者  &lt;code&gt;Spring Boot&lt;/code&gt;写过单元测试，你可能会说我们不要在写单元测试的时候用  &lt;code&gt;Spring&lt;/code&gt;。但是为什么呢？&lt;/p&gt; &lt;p&gt;考虑下面的单元测试类，这个类测试了  &lt;code&gt;RegisterUseCase&lt;/code&gt;类的单个方法：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@ExtendWith(SpringExtension.class)
@SpringBootTest
class RegisterUseCaseTest {

  @Autowired
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User(&amp;quot;zaphod&amp;quot;, &amp;quot;zaphod@mail.com&amp;quot;);
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这个测试类在我的电脑上需要大概4.5秒来执行一个空的Spring项目。&lt;/p&gt; &lt;p&gt;但是一个好的单元测试仅仅需要几毫秒。否则就会阻碍TDD（测试驱动开发）流程，这个流程倡导“测试/开发/测试”。&lt;/p&gt; &lt;p&gt;但是就算我们不使用TDD，等待一个单元测试太久也会破坏我们的注意力。&lt;/p&gt; &lt;p&gt;执行上述的测试方法事实上仅需要几毫秒。剩下的4.5秒是因为  &lt;code&gt;@SpringBootTest&lt;/code&gt;告诉了   &lt;code&gt;Spring Boot&lt;/code&gt; 要启动整个Spring Boot 应用程序上下文。&lt;/p&gt; &lt;p&gt;所以我们启动整个应用程序仅仅是因为要把  &lt;code&gt;RegisterUseCase&lt;/code&gt;实例注入到我们的测试类中。启动整个应用程序可能耗时更久，假设应用程序更大、  &lt;code&gt;Spring&lt;/code&gt;需要加载更多的实例到应用程序上下文中。&lt;/p&gt; &lt;p&gt;所以，这就是为什么不要在单元测试中使用  &lt;code&gt;Spring&lt;/code&gt;。坦白说，大部分编写单元测试的教程都没有使用  &lt;code&gt;Spring Boot&lt;/code&gt;。&lt;/p&gt; &lt;h2&gt;创建一个可测试的类实例&lt;/h2&gt; &lt;p&gt;然后，为了让  &lt;code&gt;Spring&lt;/code&gt;实例有更好的测试性，有几件事是我们可以做的。&lt;/p&gt; &lt;h3&gt;属性注入是不好的&lt;/h3&gt; &lt;p&gt;让我们以一个反例开始。考虑下述类：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@Service
public class RegisterUseCase {

  @Autowired
  private UserRepository userRepository;

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这个类如果没有  &lt;code&gt;Spring&lt;/code&gt;没法进行单元测试，因为它没有提供方法传递  &lt;code&gt;UserRepository&lt;/code&gt;实例。因此我们只能用文章之前讨论的方式-让Spring创建  &lt;code&gt;UserRepository&lt;/code&gt;实例，并通过  &lt;code&gt;@Autowired&lt;/code&gt;注解注入进去。&lt;/p&gt; &lt;p&gt;这里的教训是：不要用属性注入。&lt;/p&gt; &lt;h3&gt;提供一个构造函数&lt;/h3&gt; &lt;p&gt;实际上，我们根本不需要使用  &lt;code&gt;@Autowired&lt;/code&gt;注解：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@Service
public class RegisterUseCase {

  private final UserRepository userRepository;

  public RegisterUseCase(UserRepository userRepository) {
    this.userRepository = userRepository;
  }

  public User registerUser(User user) {
    return userRepository.save(user);
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这个版本通过提供一个允许传入  &lt;code&gt;UserRepository&lt;/code&gt;实例参数的构造函数来允许构造函数注入。在这个单元测试中，我们现在可以创建这样一个实例（或者我们之后要讨论的Mock实例）并通过构造函数注入了。&lt;/p&gt; &lt;p&gt;当创建生成应用上下文的时候，Spring会自动使用这个构造函数来初始化  &lt;code&gt;RegisterUseCase&lt;/code&gt;对象。注意，在Spring 5 之前，我们需要在构造函数上增加  &lt;code&gt;@Autowired&lt;/code&gt;注解，以便让Spring找到这个构造函数。&lt;/p&gt; &lt;p&gt;还要注意的是，现在  &lt;code&gt;UserRepository&lt;/code&gt;属性是  &lt;code&gt;final&lt;/code&gt;修饰的。这很重要，因为这样的话，应用程序生命周期时间内这个属性内容不会再变化。此外，它还可以帮我们避免变成错误，因为如果我们忘记初始化该属性的话，编译器就报错。&lt;/p&gt; &lt;h3&gt;减少模板代码&lt;/h3&gt; &lt;p&gt;通过使用  &lt;code&gt;Lombok&lt;/code&gt;的  &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;注解，我们可以让构造函数自动生成:&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@Service
@RequiredArgsConstructor
public class RegisterUseCase {

  private final UserRepository userRepository;

  public User registerUser(User user) {
    user.setRegistrationDate(LocalDateTime.now());
    return userRepository.save(user);
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;现在，我们有一个非常简洁的类，没有样板代码，可以在普通的 java 测试用例中很容易被实例化：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;class RegisterUseCaseTest {

  private UserRepository userRepository = ...;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    User user = new User(&amp;quot;zaphod&amp;quot;, &amp;quot;zaphod@mail.com&amp;quot;);
    User savedUser = registerUseCase.registerUser(user);
    assertThat(savedUser.getRegistrationDate()).isNotNull();
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;还有部分确实，就是如何模拟测试类所依赖的  &lt;code&gt;UserReposity&lt;/code&gt;实例，我们不想依赖真实的类，因为这个类需要一个数据库连接。&lt;/p&gt; &lt;h2&gt;使用Mockito来模拟依赖项&lt;/h2&gt; &lt;p&gt;现在事实上的标准模拟库是   &lt;code&gt;Mockito&lt;/code&gt;。它提供至少两种方式来创建一个模拟  &lt;code&gt;UserRepository&lt;/code&gt;实例，来填补前述代码的空白。&lt;/p&gt; &lt;h3&gt;使用普通  &lt;code&gt;Mockito&lt;/code&gt;来模拟依赖&lt;/h3&gt; &lt;p&gt;第一种方式是使用Mockito编程：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;private UserRepository userRepository = Mockito.mock(UserRepository.class);&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这会从外界创建一个看起来像  &lt;code&gt;UserRepository&lt;/code&gt;的对象。默认情况下，方法被调用时不会做任何事情，如果方法有返回值，会返回  &lt;code&gt;null&lt;/code&gt;。&lt;/p&gt; &lt;p&gt;因为  &lt;code&gt;userRepository.save(user)&lt;/code&gt;返回null，现在我们的测试代码  &lt;code&gt;assertThat(savedUser.getRegistrationDate()).isNotNull()&lt;/code&gt;会报空指针异常（NullPointerException）。&lt;/p&gt; &lt;p&gt;所以我们需要告诉  &lt;code&gt;Mockito&lt;/code&gt;，当  &lt;code&gt;userRepository.save(user)&lt;/code&gt;调用的时候返回一些东西。我们可以用静态的  &lt;code&gt;when&lt;/code&gt;方法实现：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@Test
void savedUserHasRegistrationDate() {
  User user = new User(&amp;quot;zaphod&amp;quot;, &amp;quot;zaphod@mail.com&amp;quot;);
  when(userRepository.save(any(User.class))).then(returnsFirstArg());
  User savedUser = registerUseCase.registerUser(user);
  assertThat(savedUser.getRegistrationDate()).isNotNull();
}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这会让  &lt;code&gt;userRepository.save()&lt;/code&gt;返回和传入对象相同的对象。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Mockito&lt;/code&gt;为了模拟对象、匹配参数以及验证方法调用，提供了非常多的特性。想看更多，  &lt;a href="https://www.javadoc.io/doc/org.mockito/mockito-core/2.23.4/org/mockito/Mockito.html" rel="nofollow noreferrer"&gt;文档&lt;/a&gt;&lt;/p&gt; &lt;h3&gt;通过  &lt;code&gt;Mockito&lt;/code&gt;的  &lt;code&gt;@Mock&lt;/code&gt;注解模拟对象&lt;/h3&gt; &lt;p&gt;创建一个模拟对象的第二种方式是使用  &lt;code&gt;Mockito&lt;/code&gt;的  &lt;code&gt;@Mock&lt;/code&gt;注解结合 JUnit Jupiter的  &lt;code&gt;MockitoExtension&lt;/code&gt;一起使用：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  private RegisterUseCase registerUseCase;

  @BeforeEach
  void initUseCase() {
    registerUseCase = new RegisterUseCase(userRepository);
  }

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;  &lt;code&gt;@Mock&lt;/code&gt;注解指明那些属性需要  &lt;code&gt;Mockito&lt;/code&gt;注入模拟对象。由于  &lt;code&gt;JUnit&lt;/code&gt;不会自动实现，  &lt;code&gt;MockitoExtension&lt;/code&gt;则告诉  &lt;code&gt;Mockito&lt;/code&gt;来评估这些  &lt;code&gt;@Mock&lt;/code&gt;注解。&lt;/p&gt; &lt;p&gt;这个结果和调用  &lt;code&gt;Mockito.mock()&lt;/code&gt;方法一样，凭个人品味选择即可。但是请注意，通过使用   &lt;code&gt;MockitoExtension&lt;/code&gt;，我们的测试用例被绑定到测试框架。&lt;/p&gt; &lt;p&gt;我们可以在  &lt;code&gt;RegisterUseCase&lt;/code&gt;属性上使用  &lt;code&gt;@InjectMocks&lt;/code&gt;注解来注入实例，而不是手动通过构造函数构造。  &lt;code&gt;Mockito&lt;/code&gt;会使用特定的  &lt;a href="https://www.javadoc.io/doc/org.mockito/mockito-core/2.23.4/org/mockito/InjectMocks.html" rel="nofollow noreferrer"&gt;算法&lt;/a&gt;来帮助我们创建相应实例对象：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;@ExtendWith(MockitoExtension.class)
class RegisterUseCaseTest {

  @Mock
  private UserRepository userRepository;

  @InjectMocks
  private RegisterUseCase registerUseCase;

  @Test
  void savedUserHasRegistrationDate() {
    // ...
  }

}&lt;/code&gt;&lt;/pre&gt; &lt;h2&gt;使用AssertJ创建可读断言&lt;/h2&gt; &lt;p&gt;  &lt;code&gt;Spring Boot&lt;/code&gt; 测试包自动附带的另一个库是  &lt;code&gt;AssertJ&lt;/code&gt;。我们在上面的代码中已经用到它进行断言：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;assertThat(savedUser.getRegistrationDate()).isNotNull();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;然而，有没有可能让断言可读性更强呢？像这样，例子：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;assertThat(savedUser).hasRegistrationDate();&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;有很多测试用例，只需要像这样进行很小的改动就能大大提高可理解性。所以，让我们在test/sources中创建我们自定义的断言吧：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;class UserAssert extends AbstractAssert&amp;lt;UserAssert, User&amp;gt; {

  UserAssert(User user) {
    super(user, UserAssert.class);
  }

  static UserAssert assertThat(User actual) {
    return new UserAssert(actual);
  }

  UserAssert hasRegistrationDate() {
    isNotNull();
    if (actual.getRegistrationDate() == null) {
      failWithMessage(
        &amp;quot;Expected user to have a registration date, but it was null&amp;quot;
      );
    }
    return this;
  }
}&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;现在，如果我们不是从  &lt;code&gt;AssertJ&lt;/code&gt;库直接导入，而是从我们自定义断言类  &lt;code&gt;UserAssert&lt;/code&gt;引入  &lt;code&gt;assertThat&lt;/code&gt;方法的话，我们就可以使用新的、更可读的断言。&lt;/p&gt; &lt;p&gt;创建一个这样自定义的断言类看起来很费时间，但是其实几分钟就完成了。我相信，将这些时间投入到创建可读性强的测试代码中是值得的，即使之后它的可读性只有一点点提高。我们编写测试代码就一次，但是之后，很多其他人（包括未来的我）在软件生命周期中，需要阅读、理解然后操作这些代码很多次。&lt;/p&gt; &lt;p&gt;如果你还是觉得很费事，可以看看  &lt;a href="http://joel-costigliola.github.io/assertj/assertj-assertions-generator.html" rel="nofollow noreferrer"&gt;断言生成器&lt;/a&gt;&lt;/p&gt; &lt;h2&gt;结论&lt;/h2&gt; &lt;p&gt;尽管在测试中启动Spring应用程序也有些理由，但是对于一般的单元测试，它不必要。有时甚至有害，因为更长的周转时间。换言之，我们应该使用更容易支持编写普通单元测试的方式构建Spring实例。&lt;/p&gt; &lt;p&gt;  &lt;code&gt;Spring Boot Test Starter&lt;/code&gt;附带  &lt;code&gt;Mockito&lt;/code&gt;和  &lt;code&gt;AssertJ&lt;/code&gt;作为测试库。让我们利用这些测试库来创建富有表现力的单元测试!&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>springboot 单元测试 java</category>
      <guid isPermaLink="true">https://itindex.net/detail/61529-%E7%BF%BB%E8%AF%91-spring-boot</guid>
      <pubDate>Thu, 10 Jun 2021 09:18:30 CST</pubDate>
    </item>
    <item>
      <title>Openresty流量复制/AB测试/协程_jinnianshilongnian的专栏-CSDN博客</title>
      <link>https://itindex.net/detail/61522-openresty-%E6%B5%81%E9%87%8F-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;div&gt;    &lt;h2&gt;流量复制&lt;/h2&gt;    &lt;p&gt;在实际开发中经常涉及到项目的升级，而该升级不能简单的上线就完事了，需要验证该升级是否兼容老的上线，因此可能需要并行运行两个项目一段时间进行数据比对和校验，待没问题后再进行上线。这其实就需要进行流量复制，把流量复制到其他服务器上，一种方式是使用如      &lt;a href="https://github.com/session-replay-tools/tcpcopy"&gt;tcpcopy&lt;/a&gt;引流；另外我们还可以使用nginx的HttpLuaModule模块中的ngx.location.capture_multi进行并发执行来模拟复制。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;构造两个服务&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /test1 {
        keepalive_timeout 60s; 
        keepalive_requests 1000;
        content_by_lua &amp;apos;
            ngx.print(&amp;quot;test1 : &amp;quot;, ngx.req.get_uri_args()[&amp;quot;a&amp;quot;])
            ngx.log(ngx.ERR, &amp;quot;request test1&amp;quot;)
        &amp;apos;;
    }
    location /test2 {
        keepalive_timeout 60s; 
        keepalive_requests 1000;
        content_by_lua &amp;apos;
            ngx.print(&amp;quot;test2 : &amp;quot;, ngx.req.get_uri_args()[&amp;quot;a&amp;quot;])
            ngx.log(ngx.ERR, &amp;quot;request test2&amp;quot;)
        &amp;apos;;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;  &lt;/p&gt;    &lt;p&gt;通过ngx.location.capture_multi调用&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /test {
         lua_socket_connect_timeout 3s;
         lua_socket_send_timeout 3s;
         lua_socket_read_timeout 3s;
         lua_socket_pool_size 100;
         lua_socket_keepalive_timeout 60s;
         lua_socket_buffer_size 8k;

         content_by_lua &amp;apos;
             local res1, res2 = ngx.location.capture_multi{
                   { &amp;quot;/test1&amp;quot;, { args = ngx.req.get_uri_args() } },
                   { &amp;quot;/test2&amp;quot;, { args = ngx.req.get_uri_args()} },
             }
             if res1.status == ngx.HTTP_OK then
                 ngx.print(res1.body)
             end
             if res2.status ~= ngx.HTTP_OK then
                --记录错误
             end
         &amp;apos;;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;此处可以根据需求设置相应的超时时间和长连接连接池等；ngx.location.capture底层通过cosocket实现，而其支持Lua中的协程，通过它可以以同步的方式写非阻塞的代码实现。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;此处要考虑记录失败的情况，对失败的数据进行重放还是放弃根据自己业务做处理。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;h2&gt;AB测试&lt;/h2&gt;    &lt;p&gt;AB测试即多版本测试，有时候我们开发了新版本需要灰度测试，即让一部分人看到新版，一部分人看到老版，然后通过访问数据决定是否切换到新版。比如可以通过根据区域、用户等信息进行切版本。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;比如京东商城有一个cookie叫做__jda，该cookie是在用户访问网站时种下的，因此我们可以拿到这个cookie，根据这个cookie进行版本选择。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;比如两次清空cookie访问发现第二个数字串是变化的，即我们可以根据第二个数字串进行判断。&lt;/p&gt;    &lt;p&gt;__jda=122270672.1059377902.1425691107.1425691107.1425699059.1&lt;/p&gt;    &lt;p&gt;__jda=122270672.556927616.1425699216.1425699216.1425699216.1。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;判断规则可以比较多的选择，比如通过尾号；要切30%的流量到新版，可以通过选择尾号为1，3,5的切到新版，其余的还停留在老版。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;1、使用map选择版本&lt;/strong&gt; &lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;map $cookie___jda $ab_key {
    default                                       &amp;quot;0&amp;quot;;
    ~^\d+\.\d+(?P&amp;lt;k&amp;gt;(1|3|5))\.                    &amp;quot;1&amp;quot;;
}&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;使用      &lt;a href="https://nginx.org/cn/docs/http/ngx_http_map_module.html"&gt;map&lt;/a&gt;映射规则，即如果是到新版则等于&amp;quot;1&amp;quot;，到老版等于“0”； 然后我们就可以通过ngx.var.ab_key获取到该数据。&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /abtest1 {
        if ($ab_key = &amp;quot;1&amp;quot;) {
            echo_location /test1 ngx.var.args;
        }
        if ($ab_key = &amp;quot;0&amp;quot;) {
            echo_location /test2 ngx.var.args;
        }
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;此处也可以使用proxy_pass到不同版本的服务器上 &lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /abtest2 {
        if ($ab_key = &amp;quot;1&amp;quot;) {
            rewrite ^ /test1 break;
            proxy_pass http://backend1;
        }
        rewrite ^ /test2 break;
        proxy_pass http://backend2;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;2、直接在Lua中使用lua-resty-cookie获取该Cookie进行解析&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;首先下载lua-resty-cookie&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;cd /usr/example/lualib/resty/
wget https://raw.githubusercontent.com/cloudflare/lua-resty-cookie/master/lib/resty/cookie.lua&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; &lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /abtest3 {
        content_by_lua &amp;apos;

             local ck = require(&amp;quot;resty.cookie&amp;quot;)
             local cookie = ck:new()
             local ab_key = &amp;quot;0&amp;quot;
             local jda = cookie:get(&amp;quot;__jda&amp;quot;)
             if jda then
                 local v = ngx.re.match(jda, [[^\d+\.\d+(1|3|5)\.]])
                 if v then
                    ab_key = &amp;quot;1&amp;quot;
                 end
             end

             if ab_key == &amp;quot;1&amp;quot; then
                 ngx.exec(&amp;quot;/test1&amp;quot;, ngx.var.args)
             else
                 ngx.print(ngx.location.capture(&amp;quot;/test2&amp;quot;, {args = ngx.req.get_uri_args()}).body)
             end
        &amp;apos;;

    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; 首先使用      &lt;a href="https://github.com/cloudflare/lua-resty-cookie"&gt;lua-resty-cookie&lt;/a&gt;获取cookie，然后使用ngx.re.match进行规则的匹配，最后使用ngx.exec或者ngx.location.capture进行处理。此处同时使用ngx.exec和ngx.location.capture目的是为了演示，此外没有对ngx.location.capture进行异常处理。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;h2&gt;协程&lt;/h2&gt;    &lt;p&gt;Lua中没有线程和异步编程编程的概念，对于并发执行提供了协程的概念，个人认为协程是在A运行中发现自己忙则把CPU使用权让出来给B使用，最后A能从中断位置继续执行，本地还是单线程，CPU独占的；因此如果写网络程序需要配合非阻塞I/O来实现。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;ngx_lua 模块对协程做了封装，我们可以直接调用ngx.thread API使用，虽然称其为“轻量级线程”，但其本质还是Lua协程。该API必须配合该ngx_lua模块提供的非阻塞I/O API一起使用，比如我们之前使用的ngx.location.capture_multi和lua-resty-redis、lua-resty-mysql等基于cosocket实现的都是支持的。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;通过Lua协程我们可以并发的调用多个接口，然后谁先执行成功谁先返回，类似于BigPipe模型。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;1、依赖的API&lt;/strong&gt; &lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /api1 {
        echo_sleep 3;
        echo api1 : $arg_a;
    }
    location /api2 {
        echo_sleep 3;
        echo api2 : $arg_a;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; 我们使用echo_sleep等待3秒。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;2、串行实现&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /serial {
        content_by_lua &amp;apos;
            local t1 = ngx.now()
            local res1 = ngx.location.capture(&amp;quot;/api1&amp;quot;, {args = ngx.req.get_uri_args()})
            local res2 = ngx.location.capture(&amp;quot;/api2&amp;quot;, {args = ngx.req.get_uri_args()})
            local t2 = ngx.now()
            ngx.print(res1.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, res2.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, tostring(t2-t1))
        &amp;apos;;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;即一个个的调用，总的执行时间在6秒以上，比如访问http://192.168.1.2/serial?a=22&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;api1 : 22 
api2 : 22 
6.0040001869202&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;3、ngx.location.capture_multi实现&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /concurrency1 {
        content_by_lua &amp;apos;
            local t1 = ngx.now()
            local res1,res2 = ngx.location.capture_multi({
                  {&amp;quot;/api1&amp;quot;, {args = ngx.req.get_uri_args()}},
                  {&amp;quot;/api2&amp;quot;, {args = ngx.req.get_uri_args()}}

            })
            local t2 = ngx.now()
            ngx.print(res1.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, res2.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, tostring(t2-t1))
        &amp;apos;;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;直接使用ngx.location.capture_multi来实现，比如访问http://192.168.1.2/concurrency1?a=22&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;api1 : 22 
api2 : 22 
3.0020000934601&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;    &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;4、协程API实现 &lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;location /concurrency2 {
        content_by_lua &amp;apos;
            local t1 = ngx.now()
            local function capture(uri, args)
               return ngx.location.capture(uri, args)
            end
            local thread1 = ngx.thread.spawn(capture, &amp;quot;/api1&amp;quot;, {args = ngx.req.get_uri_args()})
            local thread2 = ngx.thread.spawn(capture, &amp;quot;/api2&amp;quot;, {args = ngx.req.get_uri_args()})
            local ok1, res1 = ngx.thread.wait(thread1)
            local ok2, res2 = ngx.thread.wait(thread2)
            local t2 = ngx.now()
            ngx.print(res1.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, res2.body, &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;, tostring(t2-t1))
        &amp;apos;;
    }&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;使用      &lt;a href="https://wiki.nginx.org/HttpLuaModule#ngx.thread.spawn"&gt;ngx.thread.spawn&lt;/a&gt;创建一个轻量级线程，然后使用      &lt;a href="https://wiki.nginx.org/HttpLuaModule#ngx.thread.wait"&gt;ngx.thread.wait&lt;/a&gt;等待该线程的执行成功。比如访问http://192.168.1.2/concurrency2?a=22&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;api1 : 22 
api2 : 22 
3.0030000209808&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;   &lt;/p&gt;    &lt;p&gt;其有点类似于Java中的线程池执行模型，但不同于线程池，其每次只执行一个函数，遇到IO等待则让出CPU让下一个执行。我们可以通过下面的方式实现任意一个成功即返回，之前的是等待所有执行成功才返回。&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;local  ok, res = ngx.thread.wait(thread1, thread2)&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;Lua协程参考资料&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://www.lua.org/pil/"&gt;《Programming in Lua》&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="http://timyang.net/lua/lua-coroutine-vs-java-wait-notify/"&gt;http://timyang.net/lua/lua-coroutine-vs-java-wait-notify/&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://github.com/andycai/luaprimer/blob/master/05.md"&gt;https://github.com/andycai/luaprimer/blob/master/05.md&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://my.oschina.net/wangxuanyihaha/blog/186401"&gt;http://my.oschina.net/wangxuanyihaha/blog/186401&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="http://manual.luaer.cn/2.11.html"&gt;http://manual.luaer.cn/2.11.html&lt;/a&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61522-openresty-%E6%B5%81%E9%87%8F-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Wed, 09 Jun 2021 18:31:04 CST</pubDate>
    </item>
    <item>
      <title>springboot单元测试技术</title>
      <link>https://itindex.net/detail/61353-springboot-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-%E6%8A%80%E6%9C%AF</link>
      <description>&lt;p&gt;整个软件交付过程中，单元测试阶段是一个能够最早发现问题，并且可以重复回归问题的阶段，在单元测试阶段做的测试越充分，软件质量就越能得到保证。
具体的代码参照   &lt;a href="https://github.com/qihaiyan/springcamp/tree/master/spring-unit-test"&gt;示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-unit-test&lt;/a&gt;&lt;/p&gt;

 &lt;h2&gt;一、概述&lt;/h2&gt;

 &lt;p&gt;一个功能的全链路测试，往往要依赖于很多外部组件，如数据库、redis、kafka、第三方接口等，单元测试的执行环境有可能受网络限制没有办法访问这些外部服务。因此，我们希望通过一些技术手段，能够用单元测试技术进行完整的功能测试，而不依赖于外部服务。&lt;/p&gt;

 &lt;h2&gt;二、REST接口的测试&lt;/h2&gt;

 &lt;p&gt;springboot提供了testRestTemplate工具用于在单元测试中测试接口，该工具只需指定接口的相对路径，不需要指定域名和端口。这个特性非常有用，因为springboot的单元测试运行环境的web服务是一个随机端口，是通过下面这个注解指定的：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;以下是通过testRestTemplate测试我们开发的  &lt;code&gt;/remote&lt;/code&gt;接口的方法：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;    @Test
    public void testRemoteCallRest() {
        String resp = testRestTemplate.getForObject(&amp;quot;/remote&amp;quot;, String.class);
        System.out.println(&amp;quot;remote result : &amp;quot; + resp);
        assertThat(resp, is(&amp;quot;{\&amp;quot;code\&amp;quot;: 200}&amp;quot;));
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;h2&gt;三、第三方接口的依赖&lt;/h2&gt;

 &lt;p&gt;上面的例子中，我们的remote接口会调用一个第三方接口   &lt;code&gt;http://someservice/foo&lt;/code&gt;，我们的构建服务器中有可能受网络限制，无法访问这个第三方接口，就会导致单元测试无法执行。我们可以通过springboot提供的   &lt;code&gt;MockRestServiceServer&lt;/code&gt; 工具来解决这个问题。&lt;/p&gt;

 &lt;p&gt;首先定义一个MockRestServiceServer变量&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;private MockRestServiceServer mockRestServiceServer;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;在单元测试的初始化阶段进行初始化&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;    @Before
    public void before() {
        mockRestServiceServer = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();

        this.mockRestServiceServer.expect(manyTimes(), MockRestRequestMatchers.requestTo(Matchers.startsWithIgnoringCase(&amp;quot;http://someservice/foo&amp;quot;)))
                .andRespond(withSuccess(&amp;quot;{\&amp;quot;code\&amp;quot;: 200}&amp;quot;, MediaType.APPLICATION_JSON));

    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;这样，当我们的单元测试程序中调用  &lt;code&gt;http://someservice/foo&lt;/code&gt;接口时，就会固定返回  &lt;code&gt;{&amp;quot;code&amp;quot;: 200}&lt;/code&gt;这个返回值，而不是真正的去访问这个第三方接口。&lt;/p&gt;

 &lt;h2&gt;四、数据库的依赖&lt;/h2&gt;

 &lt;p&gt;数据库的依赖比较简单，直接使用h2这个嵌入式数据库就可以，所有的数据库操作都是在h2这个嵌入式数据库中执行的。&lt;/p&gt;

 &lt;p&gt;已gradle配置为例：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;testImplementation &amp;apos;com.h2database:h2&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;单元测试配置文件中的数据库连接使用h2:&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;spring:
  data:
    url: jdbc:h2:mem:ut;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;这样，当我们的单元测试程序中调用  &lt;code&gt;http://someservice/foo&lt;/code&gt;接口时，就会固定返回  &lt;code&gt;{&amp;quot;code&amp;quot;: 200}&lt;/code&gt;这个返回值，而不是真正的去访问这个第三方接口。&lt;/p&gt;

 &lt;h2&gt;五、redis的依赖&lt;/h2&gt;

 &lt;p&gt;网上有一个开源的redis mockserver，模仿了大部分的redis指令，我们只需要引入这个redis-mockserver即可。
最初版本是一个国人开发的，示例中引入的是老外fork的一个版本，补充了一些指令，但是找不到源码了，我又fork了一个版本，补充了setex、zscore两个指令，有需要的可以自己编译。  &lt;a href="https://github.com/qihaiyan/redis-mock"&gt;代码连接 https://github.com/qihaiyan/redis-mock&lt;/a&gt;&lt;/p&gt;

 &lt;p&gt;已gradle配置为例：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;testImplementation &amp;apos;com.github.fppt:jedis-mock:0.1.16&amp;apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;单元测试配置文件中的数据库连接使用redis mockserver:&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;spring:
  redis:
    port: 10033
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;增加一个单独的redis配置文件，用于在单元测试中启动redis mockserver：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;@TestConfiguration
public class TestRedisConfiguration {

    private final RedisServer redisServer;

    public TestRedisConfiguration(@Value(&amp;quot;${spring.redis.port}&amp;quot;) final int redisPort) throws IOException {
        redisServer = RedisServer.newRedisServer(redisPort);
    }

    @PostConstruct
    public void postConstruct() throws IOException {
        redisServer.start();
    }

    @PreDestroy
    public void preDestroy() {
        redisServer.stop();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;h2&gt;六、kafka的依赖&lt;/h2&gt;

 &lt;p&gt;spring提供了一个kafka的测试组件，可以在单元测试期间启动一个嵌入式的kafka服务EmbeddedKafka，模拟真实的kafka操作。&lt;/p&gt;

 &lt;p&gt;已gradle配置为例：&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;testImplementation &amp;quot;org.springframework.kafka:spring-kafka-test&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;通过ClassRule初始化EmbeddedKafka，有两个topic: testEmbeddedIn 和 testEmbeddedOut 。&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;    private static final String INPUT_TOPIC = &amp;quot;testEmbeddedIn&amp;quot;;
    private static final String OUTPUT_TOPIC = &amp;quot;testEmbeddedOut&amp;quot;;
    private static final String GROUP_NAME = &amp;quot;embeddedKafkaApplication&amp;quot;;

    @ClassRule
    public static EmbeddedKafkaRule embeddedKafkaRule = new EmbeddedKafkaRule(1, true, INPUT_TOPIC, OUTPUT_TOPIC);

    public static EmbeddedKafkaBroker embeddedKafka = embeddedKafkaRule.getEmbeddedKafka();

    private static KafkaTemplate&amp;lt;String, String&amp;gt; kafkaTemplate;

    private static Consumer&amp;lt;String, String&amp;gt; consumer;

    @BeforeClass
    public static void setup() {

        Map&amp;lt;String, Object&amp;gt; senderProps = KafkaTestUtils.producerProps(embeddedKafka);
        DefaultKafkaProducerFactory&amp;lt;String, String&amp;gt; pf = new DefaultKafkaProducerFactory&amp;lt;&amp;gt;(senderProps);
        kafkaTemplate = new KafkaTemplate&amp;lt;&amp;gt;(pf, true);

        Map&amp;lt;String, Object&amp;gt; consumerProps = KafkaTestUtils.consumerProps(GROUP_NAME, &amp;quot;false&amp;quot;, embeddedKafka);
        DefaultKafkaConsumerFactory&amp;lt;String, String&amp;gt; cf = new DefaultKafkaConsumerFactory&amp;lt;&amp;gt;(consumerProps);
        consumer = cf.createConsumer();
        embeddedKafka.consumeFromAnEmbeddedTopic(consumer, OUTPUT_TOPIC);
    }

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

 &lt;p&gt;在单元测试程序的配置文件中，可以指定这2个kafka的topic&lt;/p&gt;

 &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;cloud.stream.bindings:
    handle-out-0.destination: testEmbeddedOut
    handle-in-0.destination: testEmbeddedIn
    handle-in-0.group: embeddedKafkaApplication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>spring boot spring java</category>
      <guid isPermaLink="true">https://itindex.net/detail/61353-springboot-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95-%E6%8A%80%E6%9C%AF</guid>
      <pubDate>Sun, 18 Apr 2021 17:20:00 CST</pubDate>
    </item>
    <item>
      <title>为什么说左移测试能有效降低软件开发风险？</title>
      <link>https://itindex.net/detail/61140-%E6%B5%8B%E8%AF%95-%E6%9C%89%E6%95%88-%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91</link>
      <description>&lt;div&gt;  &lt;p&gt;   &lt;img alt="&amp;#20026;&amp;#20160;&amp;#20040;&amp;#35828;&amp;#24038;&amp;#31227;&amp;#27979;&amp;#35797;&amp;#33021;&amp;#26377;&amp;#25928;&amp;#38477;&amp;#20302;&amp;#36719;&amp;#20214;&amp;#24320;&amp;#21457;&amp;#39118;&amp;#38505;&amp;#65311;" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b3d097dab514ae99f8f36f2f06b1e78~tplv-k3u1fbpfcp-watermark.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;p&gt;所有企业都希望（或应该）减少与软件开发相关的风险。但是对于为安全至关紧要的行业和金融行业服务的企业，需要尽可能消除风险，并在所有其他情况下将风险降至最低。&lt;/p&gt;
  &lt;p&gt;左移测试可以降低以下关键领域的风险，下面将详细说明：&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;安全性&lt;/li&gt;
   &lt;li&gt;可见性&lt;/li&gt;
   &lt;li&gt;法规和OEM合规性&lt;/li&gt;
   &lt;li&gt;可靠性&lt;/li&gt;
   &lt;li&gt;应用变更&lt;/li&gt;
   &lt;li&gt;外包开发&lt;/li&gt;
&lt;/ul&gt;
  &lt;h1&gt;什么是“左移”？&lt;/h1&gt;
  &lt;p&gt;“左移”是将关键的测试实践移至开发生命周期的早期。这个术语尤其在敏捷、持续和DevOps计划中屡见不鲜。那么，为什么需要执行早期软件测试？&lt;/p&gt;
  &lt;p&gt;许多测试活动发生在周期的后期，需要花费更长的时间才能找出问题所在，并且需要更多的修复成本。左移是关于将缺陷的识别和预防移到较早的阶段。如果你不这样做，并在开发周期的稍后阶段等待执行测试实践，到那时，特别是非功能性业务需求（即安全性和性能测试）在代码中已根深蒂固，以至于你只能不停地打补丁而无法正确地修复它们。那么，将测试“左移”如何对软件的关键领域有所帮助？&lt;/p&gt;
  &lt;h1&gt;安全性&lt;/h1&gt;
  &lt;p&gt;受损的应用程序安全性会导致信息泄漏、停机、污损和恶意软件安装。根据Web Hacking事件数据库，这些后果占安全相关结果的61.6％。&lt;/p&gt;
  &lt;p&gt;传统的安全性方法是通过猜测和经验的结合来模拟直接攻击。传统方法由于过分依赖运气而无效且过时。使用左移测试，可以在开发时就避免这些安全风险。&lt;/p&gt;
  &lt;h1&gt;可见性&lt;/h1&gt;
  &lt;p&gt;组织面临的最大风险之一是缺乏决策依据。开发进度是否能按时完成？产品符合要求吗？现在是否需要采取任何措施来利用机会或减轻即将来临的客户问题的影响？&lt;/p&gt;
  &lt;p&gt;Parasoft的左移测试平台通过将来自每个软件开发基础架构的数据捆绑在一起，并根据已定义的公司策略应用上下文智能，从而为组织提供了前所未有的、无与伦比的、完整的可见性，从而推动了自动化的预防和控制流程，提供最高管理级别所需的商业智能。&lt;/p&gt;
  &lt;h1&gt;法规和OEM合规性&lt;/h1&gt;
  &lt;p&gt;不遵守安全关键性法规，政府或OEM法规可能导致召回、使合同无效、制定处罚或采取法律行动。尽管总金额因行业和项目而异，但它们通常是可观的。通过静态分析、覆盖率分析、过程定义和常规测量的组合，左移测试方法将合规性系统化为一个自动化的、气密性的过程，以确保控制风险。&lt;/p&gt;
  &lt;h1&gt;可靠性&lt;/h1&gt;
  &lt;p&gt;软件可靠性仍然是最显而易见的问题之一，可以通过左移测试轻松解决。崩溃、停机时间和缺少SLA的症状会严重影响公司在市场中的地位。通过将预防、检测和验证结合到一个持续改进的过程中，左移测试将确保减少或消除可靠性风险。&lt;/p&gt;
  &lt;h1&gt;应用变更&lt;/h1&gt;
  &lt;p&gt;在开发人员中，有一个老话：“如果调试是消除bug的过程，那么开发必须是将它们放入其中的过程。”用业务术语来说，每次代码更改都是一种风险。&lt;/p&gt;
  &lt;p&gt;左移测试通过执行可防止开发人员工作时出现结构和设计问题的编码策略，消除了引入新缺陷的风险。此外，左移测试策略将允许自动生成、执行和管理回归测试以及详细的覆盖率分析，从而消除损害现有功能的风险。自动化的同行评审分配可提供降低风险的最后一层，以确保适当的团队专家可以检查100％的代码。&lt;/p&gt;
  &lt;h1&gt;外包开发&lt;/h1&gt;
  &lt;p&gt;随着企业从高科技领域中廉价、缺乏经验的劳动力的风险中吸取教训，外包正逐渐消失。但是，许多企业拥有活跃的离岸开发和测试团队，并将继续沿这一道路前进。尽管策略各不相同，但成功的外包商会在现场和非现场建立多个缓解风险的门槛，以防范风险注入。左移测试很自然地适合在每个缓解风险的门口建立和实施编码标准，单元测试覆盖率以及同行评审的政策。&lt;/p&gt;
  &lt;h1&gt;那你怎么左移呢？&lt;/h1&gt;
  &lt;p&gt;为简洁起见，左移测试方法分为两个主要活动：&lt;/p&gt;
  &lt;h2&gt;应用开发测试最佳实践&lt;/h2&gt;
  &lt;p&gt;进行早期阶段的开发实践，例如静态代码分析和单元测试，有助于在过程中尽早发现并防止缺陷。&lt;/p&gt;
  &lt;p&gt;重要的是要记住，目标不是发现错误，而是减少错误的数量（尤其是那些会发布到版本中的错误）。最终，与发现更多的bug相比，首先创建更少的bug有价值得多，而且价格便宜得多。因此，通过标记可能“有效”但仍不安全的代码，采用一种主动预防性的安全关键编码标准。&lt;/p&gt;
  &lt;p&gt;编码标准是等同于工程标准的软件，它们是减少错误数量（除了更早发现错误），支持并从左移计划中获得最大价值的关键。编码标准是软件工程知识的体现，可以帮助你避免错误/危险/不安全的代码。要使用它们，你需要应用静态代码分析。&lt;/p&gt;
  &lt;p&gt;为了软件安全，这对于成功强化软件尤为重要。你想在代码中构建安全性，而不是对其进行测试。编码标准可让你从一开始就构建更安全的应用程序（即通过设计确保安全性），如果你受制于诸如   &lt;a href="https://juejin.cn/post/6911607384843091982" target="_blank"&gt;GDPR法规&lt;/a&gt;之类的要求，这会是一个好主意。&lt;/p&gt;
  &lt;h2&gt;利用服务虚拟化实现连续测试&lt;/h2&gt;
  &lt;p&gt;接下来，你必须接受在开发过程的所有阶段（包括后期）创建的测试，并不断进行下去。对于采用敏捷开发实践以在整个开发过程中提供连续反馈的团队来说，这一点至关重要。单元测试可以轻松地连续执行，但是由于外部系统的依赖性，通常很难将后期功能测试的执行转移到左手，在这里你可以利用服务虚拟化来进行连续测试。&lt;/p&gt;
  &lt;p&gt;服务虚拟化使你可以模拟可用性可能受限的相关系统，例如大型机、访问费、第三方服务或可能尚未准备就绪的系统。通过模拟它们，你可以在没有整个系统可用的情况下执行功能测试，并且可以将测试执行完全转移到开发桌面。&lt;/p&gt;
  &lt;p&gt;在性能测试方面，服务虚拟化使你可以在一切准备就绪之前进行测试，而无需对系统中的所有内容进行完整的实验。你甚至可以运行各种假设分析场景，例如，如果应用服务器运行速度快而数据库运行缓慢（在现实世界中很难实现），该怎么办。或者，如果我的服务器开始抛出一些有趣的错误，例如500错误，那将如何影响系统性能呢？&lt;/p&gt;
  &lt;p&gt;你可以随心所欲地推动系统运行，并尽早实施。&lt;/p&gt;
  &lt;p&gt;同样，你可以更早地开始进行安全测试。与物理系统解耦使你可以做一些更有趣的事情，这就是使模拟系统以不可思议的方式运行。现在，你可以真正进行安全测试了……你不仅可以在系统上查看受污染的数据和DDoS，还可以使系统充满数据包，发送格式错误的数据或攻击者常用的许多其他利用方法。因此，不仅可以进行更早的测试（左移），而且还可以比测试实验室或生产系统进行更深入的测试。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61140-%E6%B5%8B%E8%AF%95-%E6%9C%89%E6%95%88-%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91</guid>
      <pubDate>Mon, 04 Jan 2021 09:09:45 CST</pubDate>
    </item>
    <item>
      <title>Mac SSD：如何测试你的硬盘速度？「更新测速软件推荐」 - Mac玩儿法</title>
      <link>https://itindex.net/detail/61137-mac-ssd-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;div&gt;    &lt;div&gt;      &lt;div&gt;        &lt;div&gt;     &lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;      &lt;div&gt;        &lt;div&gt;&lt;/div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;p&gt;今天我们来个「酒瓶装新酒」，说说如何测试自己的 Mac 硬盘速度，之前曾介绍过用终端命令行的测试方法，但大多数朋友还是想了解一些有没有专业些的软件来做，那么下面小编就来继续讲讲~ （可直接跳入第二段）&lt;/p&gt;        &lt;h2&gt;命令行测试方法&lt;/h2&gt;        &lt;p&gt;Terminal 指令真是无所不能，就连          &lt;a href="https://www.waerfa.com/tag/ssd" target="_blank" title=""&gt;SSD&lt;/a&gt;的速度也能准确“算计”出来：&lt;/p&gt;        &lt;p&gt;          &lt;strong&gt;测试写入速度：&lt;/strong&gt;&lt;/p&gt;        &lt;pre&gt;          &lt;code&gt;time dd if=/dev/zero bs=1024k of=tstfile count=1024&lt;/code&gt;&lt;/pre&gt;        &lt;p&gt;在我的 2011 款 Macbook Air 13′ 执行测试后，输出结果是：&lt;/p&gt;        &lt;p&gt;1024+0 records in
1024+0 records out
1073741824 bytes transferred in 4.196358 secs (          &lt;strong&gt;255874697 bytes/sec&lt;/strong&gt;)&lt;/p&gt;        &lt;p&gt;real 0m4.205s
user 0m0.004s
sys 0m0.712s&lt;/p&gt;        &lt;p&gt;将          &lt;strong&gt;255874697 bytes/sec&lt;/strong&gt;连续除以两次1024就可以看到你的SSD硬盘标准写入速度了，我的为每秒244m&lt;/p&gt;        &lt;p&gt;          &lt;strong&gt;读取速度命令：&lt;/strong&gt;&lt;/p&gt;        &lt;pre&gt;          &lt;code&gt;dd if=tstfile bs=1024k of=/dev/null count=1024&lt;/code&gt;&lt;/pre&gt;        &lt;p&gt;结果：每秒256m&lt;/p&gt;        &lt;h2&gt;测速软件推荐&lt;/h2&gt;        &lt;div&gt;          &lt;a href="https://www.waerfa.com/images/image/thumb/Purple71/v4/dc/a0/59/dca05950-3a6d-5617-10be-e40843f06348/source800x500bb.jpg"&gt;            &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;"&gt;&lt;/img&gt;&lt;/a&gt;          &lt;div&gt;            &lt;div&gt;              &lt;div&gt;                &lt;div&gt;                  &lt;a href="https://www.waerfa.com/blackmagic-disk-speed-test" target="_blank"&gt;                    &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;1" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;1"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;            &lt;div&gt;              &lt;p&gt;                &lt;a href="https://www.waerfa.com/blackmagic-disk-speed-test" target="_blank"&gt;Blackmagic Disk Speed Test&lt;/a&gt;&lt;/p&gt;              &lt;p&gt;&lt;/p&gt;              &lt;div&gt;                &lt;a href="https://www.waerfa.com/blackmagic-disk-speed-test?st=download1" target="_blank"&gt;下载&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;        &lt;p&gt;这款软件想必已是许多硬件发烧友的「老朋友」了，她曾出现过各大媒体的新品 Mac 评测视频里，操作简单，BDST 界面就像一台汽车的智能仪表盘，点一下中间的「Start」按钮（像一键启动按钮）就开始读写速度模拟测试了，「读」与「写」的速度在测试中会动态变化，如果你不点「停止」（还是那个 Start 按钮），她会不停测试下去，但小编的测试结果实在有些寒酸，竟然没达到网上说的“至少 300 MB”，额…… 话说现在有的机器 SSD 速度都已达到了 GB 级别…&lt;/p&gt;        &lt;h3&gt;&lt;/h3&gt;        &lt;p&gt;          &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;2" height="491" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20455%20491'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;2" width="455"&gt;&lt;/img&gt;&lt;/p&gt;        &lt;p&gt;速度显示表盘下方可以检测出你的硬盘是否能胜任各级别视频的播放，并划出了 10 Bit YUV 10 Bit RGB 12 Bit RGB 三种颜色编码范围内的读写支持结果，这款软件的开发商本身就是做视频解码器的，所有有些格式小编都不懂唉，抱歉，同时，在右侧，你还能看到支持的视频级别在播放时硬盘的读写速度。&lt;/p&gt;        &lt;p&gt;&lt;/p&gt;        &lt;div&gt;          &lt;a href="https://www.waerfa.com/images/image/thumb/Purple60/v4/f6/4a/ee/f64aeeef-c857-9186-9640-e295717e2676/source800x500bb.jpg"&gt;            &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;3" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;3"&gt;&lt;/img&gt;&lt;/a&gt;          &lt;div&gt;            &lt;div&gt;              &lt;div&gt;                &lt;div&gt;                  &lt;a href="https://www.waerfa.com/aja-system-test-lite" target="_blank"&gt;                    &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;4" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;4"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;            &lt;div&gt;              &lt;p&gt;                &lt;a href="https://www.waerfa.com/aja-system-test-lite" target="_blank"&gt;AJA System Test Lite&lt;/a&gt;&lt;/p&gt;              &lt;p&gt;&lt;/p&gt;              &lt;div&gt;                &lt;a href="https://www.waerfa.com/aja-system-test-lite?st=download1" target="_blank"&gt;下载&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;        &lt;p&gt;&lt;/p&gt;        &lt;p&gt;这款软件也很常见，虽说不支持测试外接硬盘的速度，但提供了超多测试文件尺寸可选，而上面的「BDST」最多只能选 5GB，这个最大是 16GB，而且能够测试对指定文件的读取速度，模拟对二进制文件，视频框架文件的读写速度。在「Video Frame Size」里能选择 4K 级别的分辨率进行测试。&lt;/p&gt;        &lt;p&gt;          &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;5" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;5"&gt;&lt;/img&gt;&lt;/p&gt;        &lt;div&gt;          &lt;a href="https://www.waerfa.com/images/image/thumb/Purple118/v4/a5/71/02/a5710296-c798-7693-0530-ca184fdd9ef8/source800x500bb.jpg"&gt;            &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;6" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;6"&gt;&lt;/img&gt;&lt;/a&gt;          &lt;div&gt;            &lt;div&gt;              &lt;div&gt;                &lt;div&gt;                  &lt;a href="https://www.waerfa.com/novabench" target="_blank"&gt;                    &lt;img alt="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;7" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" title="Mac SSD&amp;#65306;&amp;#22914;&amp;#20309;&amp;#27979;&amp;#35797;&amp;#20320;&amp;#30340;&amp;#30828;&amp;#30424;&amp;#36895;&amp;#24230;&amp;#65311;&amp;#12300;&amp;#26356;&amp;#26032;&amp;#27979;&amp;#36895;&amp;#36719;&amp;#20214;&amp;#25512;&amp;#33616;&amp;#12301;&amp;#25554;&amp;#22270;7"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;            &lt;div&gt;              &lt;p&gt;                &lt;a href="https://www.waerfa.com/novabench" target="_blank"&gt;Novabench&lt;/a&gt;&lt;/p&gt;              &lt;p&gt;&lt;/p&gt;              &lt;div&gt;                &lt;a href="https://www.waerfa.com/novabench?st=download1" target="_blank"&gt;下载&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;        &lt;p&gt;这款软件是系统综合测试软件，同样能够将自己的测试结果上传到官网参与比较，最底部的「Hardware Tests」可以看到 Drive Write Speed。&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61137-mac-ssd-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Mon, 04 Jan 2021 14:33:29 CST</pubDate>
    </item>
    <item>
      <title>功能点估算法（一）_软件质量管理、项目管理、过程改进、软件自动化测试咨询与培训。-CSDN博客_功能点估算法</title>
      <link>https://itindex.net/detail/61073-%E5%8A%9F%E8%83%BD-%E7%AE%97%E6%B3%95-%E8%BD%AF%E4%BB%B6%E8%B4%A8%E9%87%8F</link>
      <description>&lt;div&gt;    &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;功能点估算法是软件项目管理众多知识中比较有技术含量的一个。在软件项目管理中项目计划制定的优劣直接关系到项目的成败，项目计划中对项目范围的估算又尤为重要，如果项目负责人对项目的规模没有一个比较客观的认识，没有对工作量、所需资源、完工时间等因素进行估算，那么项目计划也就没有存在的意义。&lt;/p&gt;    &lt;p&gt;项目范围的估算在CMMI的“MA”度量分析管理和“PP”项目计划中均有涉及，对软件项目范围的估算有很多种方法，常见的就是LOC代码行和FP功能点法，它们之间的区别和关系如下：&lt;/p&gt;    &lt;p&gt;1、 FP功能点估算法常用在项目开始或项目需求基本明确时使用，这时进行估算其结果的准确性比较高，假如这个时候使用LOC代码行估算法，则误差会比较大。&lt;/p&gt;    &lt;p&gt;2、 使用FP功能点估算法无需懂得软件使用何种开发技术。LOC代码行估算法与软件开发技术密切相关。&lt;/p&gt;    &lt;p&gt;3、 FP功能点法是以用户为角度进行估算，LOC代码行估算法则是以技术为角度进行估算的。&lt;/p&gt;    &lt;p&gt;4、 通过一些行业标准&lt;/p&gt;    &lt;p&gt;或企业自身度量的分析，FP功能点估算法是可以转换为LOC代码行的。在项目刚开始的时候进行功能点估算可以对项目的范围进行预测，在项目开发的过程中由于需求的变更和细化可能会导致项目范围的蔓延，计算出来的结果会与当初估计的不同，因此在项目结束时还需要对项目的范围情况进行估算，这个时候估算的结果才能最准确反映项目的规模。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;功能点分析的步骤&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;    在本文中将以国际标准IFPUG（International Function Point Users Group）组织提供的功能点估算法V4.1.1为基础与大家进行讲解。如下图所示，首先大家应该了解功能点估算法的使用步骤。&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p align="center"&gt;图 功能点估算的步骤 &lt;/p&gt;    &lt;p&gt;1、 识别功能点的类型。&lt;/p&gt;    &lt;p&gt;2、 识别待估算应用程序的边界和范围。&lt;/p&gt;    &lt;p&gt;3、 计算数据类型功能点所提供的未调整的功能点数量。&lt;/p&gt;    &lt;p&gt;4、 计算人机交互功能所提供的未调整的功能点数量。&lt;/p&gt;    &lt;p&gt;5、 确定调整因子。&lt;/p&gt;    &lt;p&gt;6、 计算调整后的功能点数量。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;识别项目的类型&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;国际的IFPUG组织将软件项目分为三类，功能点估算法适用于任何一类项目&lt;/p&gt;    &lt;p&gt;l 新开发项目&lt;/p&gt;    &lt;p&gt;l 二次开发的项目&lt;/p&gt;    &lt;p&gt;l 功能增强的项目&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;识别项目的范围和边界&lt;/strong&gt;    &lt;/p&gt;    &lt;p&gt;使用UML的“UseCase”用例图是以用户角度进行识别项目范围和边界的最好方法，因为在画用例图时就必须明确系统的边界。通过系统的边界我们可以知道哪些功能要计算功能点，哪些功能点是外部系统负责计算的。以下图为例：一个外贸订单系统只包含录入、修改、删除、查询和统计订单的功能，而汇率查询转换服务是不属于该系统的。&lt;/p&gt;    &lt;p&gt;    应用程序边界的识别规则大家一定要牢记，不能从技术角度去思考，必须从用户角度来定义；如果项目牵扯到多个系统，那么必须将这多个系统的边界全部描述清楚。&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p align="center"&gt;图 外贸订单系统用例图 &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;F&lt;/strong&gt;      &lt;strong&gt;P&lt;/strong&gt;      &lt;strong&gt;功能点估算分类&lt;/strong&gt;    &lt;/p&gt;    &lt;p&gt;FP功能点估算法将功能点分为以下5类：&lt;/p&gt;    &lt;p&gt;1、 ILF：Internal Logical File内部逻辑文件&lt;/p&gt;    &lt;p&gt;2、 EIF: External Interface File外部接口文件&lt;/p&gt;    &lt;p&gt;3、 EI: External Input外部输入&lt;/p&gt;    &lt;p&gt;4、 EO: External Output外部输出&lt;/p&gt;    &lt;p&gt;5、 EQ: External Inquiry外部查询其中ILF和EIF属于数据类型的功能点，EI、EO、EQ属于人机交互类型的功能点。     &lt;/p&gt;    &lt;p&gt;以外贸订单系统项目为例：&lt;/p&gt;    &lt;p&gt;l  录入订单、修改订单、删除订单是EI；&lt;/p&gt;    &lt;p&gt;l  查询订单是EO&lt;/p&gt;    &lt;p&gt;l  统计订单是EQ&lt;/p&gt;    &lt;p&gt;l  汇率查询转换系统为EIF&lt;/p&gt;    &lt;p&gt;l  订单和客户是ILF&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;识别功能点的重要原则&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;ILF、EIF要与EI、EO、EQ分开计算。对ILF和EIF复杂度的计算可以简单理解为对数据库复杂度的计算。对EI、EO、EQ复杂度的计算可以理解为对程序开发复杂度的计算。一般软件项目都是由数据和程序构成的，因此计算ILF、EIF和计算EI、EO、EQ之间没有任何关系。      &lt;strong&gt; &lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;      &lt;strong&gt;&lt;/strong&gt;       &lt;strong&gt;第二部分：内部逻辑文件与外部接口文件&lt;/strong&gt;      &lt;strong&gt;ILF&lt;/strong&gt;      &lt;strong&gt;内部逻辑文件&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;内部逻辑文件是指一组以用户角度识别的，在应用程序边界内且被维护的逻辑相关数据或控制信息。ILF的主要目的是通过应用程序的一个或多个基本处理过程来维护数据。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;EIF&lt;/strong&gt;      &lt;strong&gt;外部接口文件&lt;/strong&gt;外部接口文件是指一组在应用程序边界内被查询，但它是在其他应用程序中被维护的，以用户角度来识别的，逻辑上相关的数据。因此一个应用程序中的EIF必然是其他应用程序中的ILF。EIF的主要目的是为边界内的应用程序提供一个或多个通过基础操作过程来引用的一组数据或信息。EIF所遵循的规则：&lt;/p&gt;    &lt;p&gt;n  从用户角度出发识别的一组逻辑数据。&lt;/p&gt;    &lt;p&gt;n  这组数据是在应用程序外部，并被应用程序引用的。&lt;/p&gt;    &lt;p&gt;n  计算功能点的这个应用程序并不维护该EIF&lt;/p&gt;    &lt;p&gt;n  这组数据是作为另一个应用程序中的ILF被维护的。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;ILF&lt;/strong&gt;      &lt;strong&gt;和EIF&lt;/strong&gt;      &lt;strong&gt;复杂性计算  &lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;ILF和EIF的复杂性是取决于RET（Record element type）和DET（Data element type）的数量。DET是一个以用户角度识别的，非重复的有业务逻辑意义的字段。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;DET&lt;/strong&gt;      &lt;strong&gt;计算的规则&lt;/strong&gt;      &lt;strong&gt;：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;●  通过一个基本处理过程的执行，对ILF进行维护或从ILF/EIF中返回一个特定的、用户可识别的、非重复的字段，那么每个这样的字段算一个DET。&lt;/p&gt;    &lt;p&gt;ü  例如：添加一个外贸订单时需要保存“订单号码、订单日期、地址、邮编”，那么对于ILF订单来说它的DET就是4个。&lt;/p&gt;    &lt;p&gt;ü  例如：保存订单时还会保存订单的明细，订单的明细往往作为一个子表进行保存，那么“订单号码”在主表和子表中都同时存在（主外键），但以用户角度来识别时，存盘操作是一个最小的单位，那么订单号码只能算做一个DET。 &lt;/p&gt;    &lt;p&gt;●  当两个应用程序维护和/或引用相同的ILF/EIF，但是每个应用程序分别维护/引用它们相应的DET时，这些DET在这两个应用程序的维护或引用中将单独计算。&lt;/p&gt;    &lt;p&gt;ü  例如一个应用程序的两个“Elementary Process”基本处理过程都需要使用到“地址”的信息，地址的信息又可以细分为“国家、城市、街道、邮编”。那么对于其中一个基本处理过程来说，他将整个地址信息作为一个整体进行处理，那就只算一个DET，另外一个基本处理过程使用每个地址的详细信息，那么DET就是4个。&lt;/p&gt;    &lt;p&gt;Ø       &lt;strong&gt;RET&lt;/strong&gt;      &lt;strong&gt;计算的规则如下：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;&lt;/strong&gt;RET是指一个EIF/ILF中用户可以识别的DET的集合。如果把DET简单理解为字段的话，那RET就可以简单理解为数据库中的表。RET在ILF/EIF中分为两种类型：可选的（Optional）和必选的（Mandatory）。计算RET的规则为以下两点：&lt;/p&gt;    &lt;p&gt;●  在一个ILF/EIF中每一个可选或必选的集合都被计算为一个RET。或者&lt;/p&gt;    &lt;p&gt;●  如果一个ILF/EIF没有子集合，则ILF/EIF被计算为一个RET。&lt;/p&gt;    &lt;p&gt;ü  例如：在外贸订单系统中添加一个订单时会保存“订单信息、客户的ID、部门的ID”。&lt;/p&gt;    &lt;p&gt;那么订单系统ILF中RET为：&lt;/p&gt;    &lt;p&gt;1、 订单信息（必选的）&lt;/p&gt;    &lt;p&gt;2、 客户信息（必选的）&lt;/p&gt;    &lt;p&gt;3、 部门信息（可选的）因此ILF中RET的个数为3个。&lt;/p&gt;    &lt;p&gt;Ø       &lt;strong&gt;ILF/EIF&lt;/strong&gt;      &lt;strong&gt;复杂度的矩阵如下&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;table border="1" cellpadding="0" cellspacing="0"&gt;      &lt;tr&gt;        &lt;td width="136"&gt;          &lt;strong&gt; &lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;          &lt;strong&gt;1~19&lt;/strong&gt;          &lt;strong&gt;个DET&lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;          &lt;strong&gt;20~50&lt;/strong&gt;          &lt;strong&gt;个DET&lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;          &lt;strong&gt;超过51个DET&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td width="136"&gt;          &lt;strong&gt;1&lt;/strong&gt;          &lt;strong&gt;个RET&lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;低&lt;/td&gt;        &lt;td width="136"&gt;低&lt;/td&gt;        &lt;td width="136"&gt;中等&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td width="136"&gt;          &lt;strong&gt;2~5&lt;/strong&gt;          &lt;strong&gt;个RET&lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;低&lt;/td&gt;        &lt;td width="136"&gt;中等&lt;/td&gt;        &lt;td width="136"&gt;高&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td width="136"&gt;          &lt;strong&gt;6&lt;/strong&gt;          &lt;strong&gt;个以上RET&lt;/strong&gt;&lt;/td&gt;        &lt;td width="136"&gt;中等&lt;/td&gt;        &lt;td width="136"&gt;高&lt;/td&gt;        &lt;td width="136"&gt;高&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;div&gt;      &lt;img alt="" border="0" height="349" src="http://www.zhang-jin.net/images/Articles/FP2.jpg" width="554"&gt;&lt;/img&gt;&lt;/div&gt;    &lt;p&gt; &lt;/p&gt;    &lt;div&gt;      &lt;img alt="" border="0" height="133" src="http://www.zhang-jin.net/images/Articles/FP1.jpg" width="553"&gt;&lt;/img&gt;&lt;/div&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p align="left"&gt;       &lt;strong&gt;FP&lt;/strong&gt;      &lt;strong&gt;功能点估算法的特点&lt;/strong&gt;    &lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61073-%E5%8A%9F%E8%83%BD-%E7%AE%97%E6%B3%95-%E8%BD%AF%E4%BB%B6%E8%B4%A8%E9%87%8F</guid>
      <pubDate>Sat, 12 Dec 2020 14:49:22 CST</pubDate>
    </item>
    <item>
      <title>几种性能测试工具的总结</title>
      <link>https://itindex.net/detail/61031-%E7%A7%8D%E6%80%A7-%E6%B5%8B%E8%AF%95-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;  &lt;blockquote&gt;   &lt;p&gt;我们经常会谈论性能、并发等问题，但是衡量性能不是说写段代码循环几百次这么简单。最近从项目上的同事了解到了代码化的测试性能测试工具 k6，以及结合之前用过的Java 微基准测试 (JMH)、AB (Apache Benchmark) 测试、Jmeter 做一下总结。&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;谈性能，实际上结合实际的业务背景、网络条件、测试数据的选择等因素影响非常大，单纯的谈 QPS 等数据意义不大。&lt;/p&gt;  &lt;p&gt;这里介绍的几个工具刚好能满足平时开发工作中不同场景下衡量性能的需求，因此整理出来。&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Java 微基准测试 (JMH) 可以用于衡量一段 Java 代码到底性能如何，例如我们平时总是谈 StringBuilder 比 new String() 快很多。我们有一个很好地量化方法，就可以很直观的展示出一段代码的性能优劣。&lt;/li&gt;   &lt;li&gt;AB (Apache Benchmark) 测试是 Apache 服务器内置的一个 http web 压测工具，非常简单易用。Mac 预装了 Apache，因此可以随手使用来测试一个页面或者 API 的性能。贵在简单易用，无需额外安装。&lt;/li&gt;   &lt;li&gt;k6 一款使用 go 语言编写，支持用户编写测试脚本的测试套件。弥补了 ab 测试功能不足，以及 jemeter 不容易代码化的缺点。也是项目上需要使用，从同事那里了解到的。&lt;/li&gt;   &lt;li&gt;Jmeter 老牌的性能测试工具，有大量专门讲 jmeter 的资料，本文不再赘述。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;那我们从 JMH 开始从来看下这几个工具的特点和使用吧。&lt;/p&gt;  &lt;h3&gt;Java 微基准测试&lt;/h3&gt;  &lt;blockquote&gt;   &lt;p&gt;StringBuilder 到底比 new String() 快多少呢？&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;我们可以使用 JMH 来测试一下。JMH 是一个用于构建、运行和分析 Java 方法运行性能工具，可以做到 nano/micro/mili/macro 时间粒度。JMH 不仅可以分析 Java 语言，基于 JVM 的语言都可以使用。&lt;/p&gt;  &lt;p&gt;JMH 由 OpenJDK 团队开发，由一次下载 OpenJDK 时注意到官网还有这么一个东西。&lt;/p&gt;  &lt;p&gt;OpenJdk 官方运行 JMH 测试推的方法是使用 Maven 构建一个单独的项目，然后把需要测试的项目作为 Jar 包引入。这样能排除项目代码的干扰，得到比较可靠地测试效果。当然也可以使用 IDE 或者 Gradle 配置到自己项目中，便于和已有项目集成，代价是配置比较麻烦并且结果没那么可靠。&lt;/p&gt;  &lt;h4&gt;使用 Maven 构建基准测试&lt;/h4&gt;  &lt;p&gt;根据官网的例子，我们可以使用官网的一个模板项目。&lt;/p&gt;  &lt;p&gt;mvn archetype:generate \&lt;/p&gt;  &lt;p&gt;-DinteractiveMode=false \&lt;/p&gt;  &lt;p&gt;-DarchetypeGroupId=org.openjdk.jmh \&lt;/p&gt;  &lt;p&gt;-DarchetypeArtifactId=jmh-java-benchmark-archetype \&lt;/p&gt;  &lt;p&gt;-DgroupId=org.sample \&lt;/p&gt;  &lt;p&gt;-DartifactId=test \&lt;/p&gt;  &lt;p&gt;-Dversion=1.0&lt;/p&gt;  &lt;p&gt;创建一个项目，导入 IDE，Maven 会帮我们生成一个测试类，但是这个测试类没有任何内容，这个测试也是可以运行的。&lt;/p&gt;  &lt;p&gt;先编译成 jar&lt;/p&gt;  &lt;p&gt;mvn clean install&lt;/p&gt;  &lt;p&gt;然后使用 javar -jar 来运行测试&lt;/p&gt;  &lt;p&gt;java -jar target/benchmarks.jar&lt;/p&gt;  &lt;p&gt;运行后可以看到输出信息中包含 JDK、JVM 等信息，以及一些用于测试的配置信息。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;#JMH version: 1.22
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options:# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.MyBenchmark.testSimpleString&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;下面是一些配置信息说明&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;    &lt;p&gt;Warmup 因为 JVM 即时编译的存在，所以为了更加准确有一个预热环节，这里是预热 5，每轮 10s。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Measurement 是真实的性能测量参数，这里是 5轮，每轮10s。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Timeout 每轮测试，JMH 会进行 GC 然后暂停一段时间，默认是 10 分钟。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Threads 使用多少个线程来运行，一个线程会同步阻塞执行。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Benchmark mode 输出的运行模式，常用的有下面几个：&lt;/p&gt;    &lt;p&gt;Throughput 吞吐量，即每单位运行多少次操作。&lt;/p&gt;    &lt;p&gt;AverageTime 调用的平均时间，每次调用耗费多少时间。&lt;/p&gt;    &lt;p&gt;SingleShotTime 运行一次的时间，如果把预热关闭可以测试代码冷启动时间&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Benchmark 测试的目标类&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;实际上还有很多配置，可以通过 -h 参数查看&lt;/p&gt;  &lt;p&gt;java -jar target/benchmarks.jar -h&lt;/p&gt;  &lt;p&gt;由于默认的配置停顿的时间太长，我们通过注解修改配置，并增加了 Java 中最基本的字符串操作性能对比。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(8)
@Fork(1)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MyBenchmark {

    @Benchmark
    public void testSimpleString() {
        String s = &amp;quot;Hello world!&amp;quot;;
        for (int i = 0; i &amp;lt; 10; i++) {
            s += s;
        }
    }

    @Benchmark
    public void testStringBuilder() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i &amp;lt; 10; i++) {
            sb.append(i);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在控制台可以看到输出的测试报告，我们直接看最后一部分即可。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;Benchmark                       Mode  Cnt      Score      Error   Units
MyBenchmark.testSimpleString   thrpt   10    226.930 ±   16.621  ops/ms
MyBenchmark.testStringBuilder  thrpt   10  80369.037 ± 3058.280  ops/ms&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;Score 这列的意思是每毫秒完成了多少次操作，可见 StringBuilder 确实比普通的 String 构造器性能高很多。&lt;/p&gt;  &lt;h4&gt;更多有趣的测试&lt;/h4&gt;  &lt;p&gt;实际上平时 Java 开发中一些细节对性能有明显的影响，虽然对系统整体来说影响比较小，但是注意这些细节可以低成本的避免性能问题堆积。&lt;/p&gt;  &lt;p&gt;其中一个非常有意思细节是自动包装类型的使用，即使是一个简单的 for 循环，如果不小心讲 int 使用成 Integer 也会造成性能浪费。&lt;/p&gt;  &lt;p&gt;我们来编写一个简单的基准测试&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;@Benchmark
    public void primaryDataType() {
        int sum = 0;
        for (int i = 0; i &amp;lt; 10; i++) {
            sum += i;
        }
    }

    @Benchmark
    public void boxDataType() {
        int sum = 0;
        for (Integer i = 0; i &amp;lt; 10; i++) {
            sum += i;
        }
    }&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;运行测试后，得到下面的测试结果&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;AutoBoxBenchmark.boxDataType       thrpt    5   312779.633 ±   26761.457  ops/ms
AutoBoxBenchmark.primaryDataType   thrpt    5  8522641.543 ± 2500518.440  ops/ms&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;基本类型的性能高出了一个数量级。当然你可能会说基本类型这种性能问题比较微小，但是性能往往就是这种从细微处提高的。另外编写 JMH 测试也会让团队看待性能问题更为直观。&lt;/p&gt;  &lt;h4&gt;一份直观的 Java 基础性能报告&lt;/h4&gt;  &lt;p&gt;下面是我写的常见场景的性能测试，例如 StringBuilder 比 new String() 速度快几个数量级。&lt;/p&gt;  &lt;p&gt;   &lt;a href="https://insights.thoughtworks.cn/wp-content/uploads/2020/11/1-performance-testing-tools.png"&gt;    &lt;img alt="" src="https://insights.thoughtworks.cn/wp-content/uploads/2020/11/1-performance-testing-tools-768x839.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;代码仓库和持续更新的基准测试可以看下面的仓库。   &lt;br /&gt;   &lt;a href="https://github.com/linksgo2011/jmh-reports"&gt;https://github.com/linksgo2011/jmh-reports&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Apache Benchmark 测试&lt;/h3&gt;  &lt;blockquote&gt;   &lt;p&gt;我想用命令行快速简单的压测一下网站该怎么办呢？&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;Apache Benchmark (简称 ab，不同于产品领域的 A/B 测试) 是 Apache web 服务器自带的性能测试工具，在 windows 或者 linux 上安装了 Apache 服务器就可以在其安装位置的 bin 目录中找到 ab 这个程序。&lt;/p&gt;  &lt;p&gt;ab 使用起来非常简单，一般只需要 -n 参数指明发出请求的总数，以及 -c 参数指明测试期间的并发数。&lt;/p&gt;  &lt;p&gt;例如对 ThoughtWorks 官网首页发出 100 个请求，模拟并发数为 10：&lt;/p&gt;  &lt;p&gt;ab -n 100 -c 10   &lt;a href="https://thoughtworks.com/"&gt;https://thoughtworks.com/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;需要注意的是 ab 工具接收一个 url 作为参数，仅仅是一个域名是不合法的，需要增加 / 表示首页。稍等片刻后就可以看到测试报告:&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;Server Software:        nginx/1.15.6
Server Hostname:        thoughtworks.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Server Temp Key:        ECDH P-256 256 bits
TLS Server Name:        thoughtworks.com

Document Path:          /
Document Length:        162 bytes

Concurrency Level:      10
Time taken for tests:   42.079 seconds
Complete requests:      100
Failed requests:        0
Non-2xx responses:      100
Total transferred:      42500 bytes
HTML transferred:       16200 bytes
Requests per second:    2.38 [#/sec] (mean)
Time per request:       4207.888 [ms] (mean)
Time per request:       420.789 [ms] (mean, across all concurrent requests)
Transfer rate:          0.99 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:     1056 2474 3006.1   1144   23032
Processing:   349  740 1003.5    379    8461
Waiting:      349  461 290.9    377    2265
Total:       1411 3214 3273.9   1674   23424

Percentage of the requests served within a certain time (ms)
  50%   1674
  66%   2954
  75%   3951
  80%   4397
  90%   6713
  95%   9400
  98%  14973
  99%  23424
 100%  23424 (longest request)&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;从这个报告中可以看到服务器的一些基本信息，以及请求的统计信息。比较重要的指标是 Requests per second 每秒钟完成的请求数量，不严格的说也就是我们的平时说的 QPS。&lt;/p&gt;  &lt;p&gt;ab 测试是专为 http 请求设计的，因此 ab 的其他参数和 curl 的参数比较类似，也可以指定 http method 以及 cookies 等参数。&lt;/p&gt;  &lt;h3&gt;K6 测试套件&lt;/h3&gt;  &lt;blockquote&gt;   &lt;p&gt;我需要编写复杂的测试脚本，并保留压测的脚本、参数、数据，以及版本化该怎么做呢？&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;k6 是一个压力测试套件，使用 golang 编写。主要特性有：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;提供了友好的 CLI 工具&lt;/li&gt;   &lt;li&gt;使用 JavaScript 代码编写测试用例&lt;/li&gt;   &lt;li&gt;可以根据性能条件设置阈值，表明成功还是失败&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;k6 没有使用 nodejs 而是 golang 程序，通过包裹了一个 JavaScript 运行时来运行 JavaScript 脚本，因此不能直接使用 npm 包以及 Nodejs 提供的一些 API。&lt;/p&gt;  &lt;p&gt;同时，k6 在运行测试时，没有启动浏览器，主要用于测试页面以及 API 加载速度。k6 提供了通过网络请求（HAR）生成测试脚本的方法，实现更简便的测试脚本编写，以及 session 的维护。&lt;/p&gt;  &lt;h4&gt;使用&lt;/h4&gt;  &lt;p&gt;在 Mac 上比较简单，直接使用 HomeBrew 即可安装：&lt;/p&gt;  &lt;p&gt;brew install k6&lt;/p&gt;  &lt;p&gt;其他平台官网也提供了相应的安装方式，比较特别的是提供了 Docker 的方式运行。&lt;/p&gt;  &lt;p&gt;直接使用 k6 的命令运行测试，官网提供了一个例子：&lt;/p&gt;  &lt;p&gt;k6 run github.com/loadimpact/k6/samples/http_get.js&lt;/p&gt;  &lt;p&gt;也可以编写自己的测试脚本:&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;import http from &amp;quot;k6/http&amp;quot;;
import { sleep } from &amp;quot;k6&amp;quot;;

export default function() {
  http.get(&amp;quot;https://www.thoughtworks.com/&amp;quot;);
  sleep(1);
};&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;保存文件 script.js 后运行 k6 命令&lt;/p&gt;  &lt;p&gt;k6 run script.js&lt;/p&gt;  &lt;p&gt;然后可以看到 http 请求的各项指标&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;/\      |‾‾|  /‾‾/  /‾/   
     /\  /  \     |  |_/  /  / /    
    /  \/    \    |      |  /  ‾‾\  
   /          \   |  |‾\  \ | (_) | 
  / __________ \  |__|  \__\ \___/ .io

  execution: local
     output: -
     script: k6.js

    duration: -,  iterations: 1
         vus: 1, max: 1

    done [==========================================================] 1 / 1

    data_received..............: 108 kB 27 kB/s
    data_sent..................: 1.0 kB 252 B/s
    http_req_blocked...........: avg=2.35s    min=2.35s    med=2.35s    max=2.35s    p(90)=2.35s    p(95)=2.35s   
    http_req_connecting........: avg=79.18ms  min=79.18ms  med=79.18ms  max=79.18ms  p(90)=79.18ms  p(95)=79.18ms 
    http_req_duration..........: avg=639.03ms min=639.03ms med=639.03ms max=639.03ms p(90)=639.03ms p(95)=639.03ms
    http_req_receiving.........: avg=358.12ms min=358.12ms med=358.12ms max=358.12ms p(90)=358.12ms p(95)=358.12ms
    http_req_sending...........: avg=1.79ms   min=1.79ms   med=1.79ms   max=1.79ms   p(90)=1.79ms   p(95)=1.79ms  
    http_req_tls_handshaking...: avg=701.46ms min=701.46ms med=701.46ms max=701.46ms p(90)=701.46ms p(95)=701.46ms
    http_req_waiting...........: avg=279.12ms min=279.12ms med=279.12ms max=279.12ms p(90)=279.12ms p(95)=279.12ms
    http_reqs..................: 1      0.249921/s
    iteration_duration.........: avg=4s       min=4s       med=4s       max=4s       p(90)=4s       p(95)=4s      
    iterations.................: 1      0.249921/s
    vus........................: 1      min=1 max=1
    vus_max....................: 1      min=1 max=1&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;k6 提供的性能指标相对 ab 工具多很多，也可以通过脚本自己计算性能指标。和 ab 工具中表明每秒钟处理完的请求数是 http_reqs，上面的测试默认只有一个用户的一次请求，如果通过参数增加更多请求，可以看到和 ab 工具得到的结果比较接近。&lt;/p&gt;  &lt;p&gt;运行压力测试时，需要增加更多的虚拟用户（VU），vus 参数和持续时间的参数:&lt;/p&gt;  &lt;p&gt;k6 run --vus 10 --duration 30s script.js&lt;/p&gt;  &lt;h4&gt;编写测试脚本的一些规则&lt;/h4&gt;  &lt;p&gt;   &lt;code&gt;default&lt;/code&gt;方法是用于给每个 VU 以及每次迭代重复运行的，因此需要把真正的测试代码放到这个方法中，例如访问某个页面。&lt;/p&gt;  &lt;p&gt;为了保证测试的准确性，一些初始化的代码不应该放到   &lt;code&gt;default&lt;/code&gt;方法中。尤其是文件的读取等依赖环境上下文的操作不能放到   &lt;code&gt;default&lt;/code&gt;方法中执行，这样做也会丢失 k6 分布式运行的能力。&lt;/p&gt;  &lt;p&gt;前面提到的命令行参数，例如指定虚拟用户数量   &lt;code&gt;--vus 10&lt;/code&gt;，这些参数也可以放到脚本代码中。通过暴露一个 options 对象即可。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;export let options = {
  vus: 10,
  duration: &amp;quot;30s&amp;quot;
};
为了更为真实的模拟用户访问的场景，k6 提供了在整个测试期间让用户数量和访问时间呈阶段性变化的能力。只需要在 options 中增加 stages 参数即可：
export let options = {
 stages: [
    { duration: &amp;quot;30s&amp;quot;, target: 20 },
    { duration: &amp;quot;1m30s&amp;quot;, target: 10  },
    { duration: &amp;quot;20s&amp;quot;, target: 0 },
  ]
};&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在测试过程中需要检查网络请求是否成功，返回的状态码是否正确，以及响应时间是否符合某个阈值。在脚本中可以通过调用 check() 方法编写检查语句，以便 k6 能收集到报告。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;import http from &amp;quot;k6/http&amp;quot;;
import { check, sleep } from &amp;quot;k6&amp;quot;;

export let options = {
  vus: 10,
  duration: &amp;quot;30s&amp;quot;
};

export default function() {
  let res = http.get(&amp;quot;https://www.thoughtworks.com/&amp;quot;);
  check(res, {
    &amp;quot;status was 200&amp;quot;: (r) =&amp;gt; r.status == 200,
    &amp;quot;transaction time OK&amp;quot;: (r) =&amp;gt; r.timings.duration &amp;lt; 200
  });
  sleep(1);
};&lt;/code&gt;&lt;/pre&gt;  &lt;h4&gt;报告输出&lt;/h4&gt;  &lt;p&gt;k6 默认将报告输出到 stdout 控制台，同时也提供了多种格式报告输出，包括：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;JSON&lt;/li&gt;   &lt;li&gt;CSV&lt;/li&gt;   &lt;li&gt;InfluxDB&lt;/li&gt;   &lt;li&gt;Apache Kafka&lt;/li&gt;   &lt;li&gt;StatsD&lt;/li&gt;   &lt;li&gt;Datadog&lt;/li&gt;   &lt;li&gt;Load Impact cloud platform&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;当然，我们在编写测试的时候不可能只有一个用例，对多个场景可以在脚本中通过   &lt;code&gt;group&lt;/code&gt;进行分组，分组后输出的报告会按照分组排列。同时，也可以使用对一个组整体性能衡量的指标   &lt;code&gt;group_duration&lt;/code&gt;。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;import { group } from &amp;quot;k6&amp;quot;;

export default function() {
  group(&amp;quot;user flow: returning user&amp;quot;, function() {
    group(&amp;quot;visit homepage&amp;quot;, function() {
      // load homepage resources
    });
    group(&amp;quot;login&amp;quot;, function() {
      // perform login
    });
  });
};&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;InfluxDB 等外部数据收集平台时，还可以打上标签，供过滤和检索使用。k6 提供了一些内置的标签，并允许用户自定义标签。&lt;/p&gt;  &lt;h3&gt;总结&lt;/h3&gt;  &lt;p&gt;实际上用于性能测试的工具还有很多，也有一些专门的工具针对网络质量（iperf） 、数据库（sysbench）、前端页面（PageSpeed）等专门方面进行性能测试。&lt;/p&gt;  &lt;p&gt;写本文的初衷是想说评价性能，以及做性能优化的第一步应该是寻找到合适工具做一次基准测试，这样的优化往往才有意义。我在使用 JMH 后不仅在工作中使用它对一些代码片段进行测试以及优化，同时更重要的是，在codereview 中对某些操作关于性能的讨论不再基于经验，而是事实。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;p&gt;   &lt;strong&gt;更多精彩洞见，请关注微信公众号：ThoughtWorks洞见&lt;/strong&gt;&lt;/p&gt;  &lt;div&gt;   &lt;div&gt;    &lt;a href="https://www.addtoany.com/add_to/wechat?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="WeChat"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/sina_weibo?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Sina Weibo"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/evernote?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Evernote"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/pocket?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Pocket"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/instapaper?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Instapaper"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Email"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="LinkedIn"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fperformance-testing-tools%2F&amp;linkname=%E5%87%A0%E7%A7%8D%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E7%9A%84%E6%80%BB%E7%BB%93" rel="nofollow noopener" target="_blank" title="Pinterest"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/share"&gt;     &lt;img alt="Share" src="https://static.addtoany.com/buttons/favicon.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/61031-%E7%A7%8D%E6%80%A7-%E6%B5%8B%E8%AF%95-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Wed, 25 Nov 2020 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>无代码化的测试自动化</title>
      <link>https://itindex.net/detail/60939-%E4%BB%A3%E7%A0%81-%E6%B5%8B%E8%AF%95-%E8%87%AA%E5%8A%A8%E5%8C%96</link>
      <description>&lt;div&gt;    &lt;p&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;2020年软件测试自动化的趋势除了智能化、云化、敏捷化/DevOps化、模型化等，还有一个亮眼的存在：Codeless Test Automation，即无代码化的测试自动化。不是没有代码，而是测试人员不用自己开发测试代码，使用Codeless测试工具可以帮助我们生成可以执行的测试用例集。如此将大大降低自动化测试的技术门槛，没有编程经验的测人员甚至是业务分析人员也可以很快上手，是不是令人心动？&lt;/p&gt;    &lt;p&gt;实际上，这不仅是软件测试的一个新趋势，而且是整个软件工程的一个新趋势：无代码化的软件应用，比如国际上比较流行的无代码化网站创建工具包括Wix、Squarespace等。软件测试正是顺应这一趋势，出现了一些无代码化的测试工具。&lt;/p&gt;    &lt;p&gt;在目前的软件测试中，为了达到一个比较高的测试自动化水平，测试人员还是有很多工作要做的，比如搭建测试环境、设计测试用例、开发测试脚本，有的组织还自己开发自动化测试工具或框架，这些几乎都需要手工完成，测试自动化也仅仅体现在测试执行的自动化上，开发测试脚本、适配到不同的软件版本、不同的浏览器（UI自动化测试），以及调试代码让其能够稳定运行一般都要花费不少时间。因此，即使在测试自动化水平比较高的团队里，软件测试也难免会成为软件快速交付的瓶颈。&lt;/p&gt;    &lt;p&gt;当一个团队在单元测试方面投入不够，只能基于Selenium、Appium这样的测试工具来编写大量端到端的UI自动化测试脚本，团队里的开发人员一般是不负责的，就要求测试人员具备一定的编程能力，对于很多组织来说，大多数软件测试人员的编程能力比较弱，这也拖累了自动化水平的提高和面向测试自动化的转型。&lt;/p&gt;    &lt;p&gt;Codeless自动化测试工具的出现正是为了解决上述难题，这类工具一般有两个核心特点：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;提供友好的界面，测试人员不需要编写代码即可通过界面上的操作完成测试用例的开发。&lt;/li&gt;      &lt;li&gt;通过人工智能（AI）和机器学习算法使测试用例具有自愈机制，能够自动进化和完善，自动修复和维护测试脚本中的对象和元素定位。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;Codeless自动化测试工具能够带来的好处也显而易见，这就是更高的测试覆盖率和更短的软件交付周期。不仅节省测试脚本的开发时间，也节省调试时间，而且提升测试代码的可重用性，可以跨项目跨版本重用测试代码，而不需要手动更新和调试测试代码。此外，也有利于促进敏捷团队中不同技能和职责的团队成员参与软件测试，比如团队中的业务分析人员。&lt;/p&gt;    &lt;p&gt;下面列举一些Codeless自动化测试工具。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;1.  Katalon Studio：&lt;/strong&gt;&lt;/p&gt;    &lt;br /&gt;Katalon Studio是无代码化的测试工具里面最值得关注的，它是2015年推出的一个自动化测试框架，目前在国外各类机构的Top自动化测试工具排行榜中都排名靠前。另外，它的开源属性（也有收费版本）也大大促进了该工具的普及和发展，不过目前还没有中文版本。    &lt;p&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;Katalon Studio使用Selenium和Appium作为底层框架，支持Web和Android、iOS移动应用的UI自动化测试，支持多种主流浏览器。也支持Restful和Soap协议的API接口自动化测试。作为无代码的测试工具，既支持有编程经验的测试人员使用Groovy语言开发测试脚本，同时也支持没有编程经验的测试人员开发测试用例。&lt;/p&gt;    &lt;p&gt;在UI自动化测试方面，它提供录制-回放功能，Web recorder utility接收应用程序上的所有动作，转化成测试用例。也提供object spy功能在界面上捕获元素对象来支持用户自己编写测试用例。&lt;/p&gt;    &lt;p&gt;在最新的7.6版本中，Katalon Studio提供了UI测试用例自愈（self-healing）功能：在测试用例运行时，当使用缺省的定位方法（比如XPath）定位不到这个元素时，工具会自动尝试其它的定位方式进行元素定位（比如CSS），让测试得以运行，并在随后的测试中也使用新的定位方式。测试结束后会建议更新测试用例：用新的定位方式代替不工作的定位方式。但使用这个功能需要企业版的License。至于这个功能是不是通过AI技术实现的，在Katalon Studio的官方指南中并没有强调。&lt;/p&gt;    &lt;p&gt;当然，作为一个优秀的测试工具的标配，Katalon Studio提供多种plug-in支持和Jira、Git、Jenkins、Jmeter、Sauce Labs等多款工具的集成，实现和测试管理、缺陷管理和持续集成管理的集成。&lt;/p&gt;    &lt;p&gt;对于Katalon Studio的功能，后续还会专门介绍。&lt;/p&gt;    &lt;p&gt;            &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;2.  TestCraft：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;TestCraft是一款商业软件，以SaaS的模式为Web应用提供自动化测试服务，用户通过账号登录Web管理界面，因此也是一款云化的测试工具。底层也是基于Selenium框架。TestCraft通过两种方式生成测试用例：一种是通过图形界面建模生成、调整测试步骤，等功能实现后再为每个测试步骤添加控件元素。因此，这也可以说是一款模型化（MBT）测试工具——在需求分析阶段就创建测试步骤，有助于团队内部沟通澄清需求。另一种是在软件功能实现以后通过录制—回放生成测试用例。&lt;/p&gt;    &lt;p&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;TestCraft也支持所有主流的浏览器，可以同时在多个浏览器上运行测试；为一个测试用例创建多个测试数据集；有定时执行和测试结果通知功能，为一个测试用例创多个测试数据集；也支持和CI/CD管理工具像Jenkins的集成，以及和Jira集成。TestCraft也提供了控件的动态重新绑定机制——“on-the-fly rebinding”，在测试执行过程中修复元素定位。优点：    &lt;ul&gt;      &lt;li&gt;为每一个测试用例创建一个模型，直观的展示测试执行的路径，适合设计复杂的测试场景。&lt;/li&gt;      &lt;li&gt;提供“智能绑定”式定位器的推荐和自我修复。缺点：只能使用专有的框架，无法导入/导出测试脚本。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;3.  Perfecto&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;Perfecto是一款商业软件，提供云化的测试自动化解决方案，用于Web和移动应用的测试。它远程提供多款手机及平板真机，支持在远程iOS和Android设备上进行手工或自动化测试，可以在多台设备上并行运行自动化测试。基于录制-回放的无代码化测试用例开发是Perfecto提供的功能之一，如下图所示，实时捕捉界面上的操作在左边生成和调整测试步骤。&lt;/p&gt;    &lt;p&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;大家有兴趣可以到其官网上看一下demo:https://www.perfecto.io/codeless-automation。基于AI的自愈功能让测试脚本能够连续运行，自我完善。另外它还提供基于AI技术的测试分析和缺陷分类,帮助快速定位缺陷。总之，值得大家去深入学习它所提供的这些智能化的功能。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;4.  TestingWhiz&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;TestingWhiz也可以支持Web及移动端的UI自动化测试，以及Web Service的API测试。基于关键字和数据驱动测试用例。它提供的Visual Recorder可以支持桌面应用、flash应用的元素识别和web UI测试。TestingWhiz提供recorder功能可以录制和存储web应用控件，桌面应用控件，以及移动应用的控件。&lt;/p&gt;    &lt;p&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;&lt;/p&gt;     &lt;p&gt;除上述工具之外，还有CloudQA、TestProject、Mabl等其它的Codeless测试工具，就不一一介绍了。&lt;/p&gt;    &lt;p&gt;其实，基于录制-回放技术的UI自动化测试工具很早就有，当时主要针对桌面应用，也可以认为它们是“无代码化”测试工具的前身。在国际敏捷联盟网站整理的“Agile Practices Timeline”（敏捷实践编年史）也有这类工具的相关记载：&lt;/p&gt;    &lt;h3&gt;      &lt;strong&gt;1990:&lt;/strong&gt;&lt;/h3&gt;Testingdiscipline dominated by “black box” techniques, in particular in the form of“capture and replay” testing tools （    &lt;strong&gt;1990年：&lt;/strong&gt;黑盒（black box）测试技术在测试学科中占据了主导地位，尤其是“捕获与回放”类型的测试工具。）1988-1990:    &lt;h3&gt;&lt;/h3&gt;The rise of event-driven GUI software and their specific testingchallenges create an opportunity for “capture and replay” test automation toolsprovided by companies such as Segue or Mercury; this type of tool dominates themarket for the next decade.（    &lt;strong&gt;1988年-1990年：&lt;/strong&gt;事件驱动的GUI软件的兴起及其特定的测试方面的挑战为“捕获和回放”类测试自动化工具创造了机会。这类工具由Segue、Mercury等公司开发，并在今后10年间占据了市场主导地位。）    &lt;strong&gt;1997:&lt;/strong&gt;    &lt;h3&gt;&lt;/h3&gt;The testing tool JUnit is written by Beck and Gamma, inspired byBeck’s earlier work on SUnit; its growing popularity over the next few yearsmarks the end of the “capture and replay” era.（    &lt;strong&gt;1997年：&lt;/strong&gt;Beck和Gamma合作开发了测试工具JUnit，灵感来自Beck早期开发的工具SUnit。JUnit在未来几年日益流行，标志着测试工具“捕获和回放”时代的落幕。）    &lt;p&gt;这样看起来无代码化也不是一个新生事物，让人不得不感慨软件测试也经历了一次轮回。想起20年前使用Silk Test做桌面应用的UI自动化测试的痛苦经历：几乎每个操作系统上的测试脚本都需要重新适配，有了新的软件版本也经常不得不重新调试测试脚本，尝试了一年终于放弃……。传统的录制-回放测试工具代码结构化差，不支持数据驱动，对测试用例组织和维护方面做得差。整个测试生态当然也不如现在，现在很多工具都支持和其它工具的集成，自己不具备的功能可以通过plug-in和其它工具进行集成。&lt;/p&gt;    &lt;p&gt;另外，功能好不好用关键还在于实现的细节。这里简单对比一下Selenium IDE和Katalon Studio的录制-回放功能。&lt;/p&gt;    &lt;table cellpadding="0" cellspacing="0"&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;          &lt;br /&gt;&lt;/td&gt;        &lt;td valign="top" width="217"&gt;          &lt;p&gt;            &lt;strong&gt;Selenium IDE&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="236"&gt;          &lt;p&gt;            &lt;strong&gt;Katalon Studio&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;          &lt;p&gt;支持类型&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="203"&gt;          &lt;p&gt;            &lt;strong&gt;Web browser&lt;/strong&gt;: Chrome,Firefox&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="236"&gt;          &lt;p&gt;            &lt;strong&gt;Web Browser&lt;/strong&gt;: Chrome, Firefox, IE,Edge Chromium&lt;/p&gt;          &lt;p&gt;            &lt;strong&gt;Mobile&lt;/strong&gt;: Android, iOS&lt;/p&gt;          &lt;p&gt;            &lt;strong&gt;Windows桌面应用&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;          &lt;p&gt;Web browser录制功能&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="217"&gt;          &lt;p&gt;先安装所支持的web browser，添加对应的Selenium IDE plug-in。&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="236"&gt;          &lt;p&gt;不需要事先安装web browser，录制测试脚本时在Katalon Studio界面上选择一种web browser&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;          &lt;p&gt;录制&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="217"&gt;          &lt;p&gt;实时生成每一个测试步骤&lt;/p&gt;&lt;/td&gt;        &lt;td valign="top" width="236"&gt;实时生成每一个测试步骤，并在浏览器上同时捕获操作的界面元素，录制完成后存储到object repository中供编辑和重用&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;脚本编辑&lt;/td&gt;        &lt;td valign="top" width="217"&gt;可以对测试步骤和输入数据增加、删除、修改&lt;/td&gt;        &lt;td valign="top" width="236"&gt;可以对测试步骤和输入数据增加、删除、修改。支持的关键字比较多，也支持多种丰富脚本逻辑的statement，比如if,else, for, while等&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;测试脚本执行&lt;/td&gt;        &lt;td valign="top" width="217"&gt;只能在录制脚本的web browser运行&lt;/td&gt;        &lt;td valign="top" width="236"&gt;可以选择任一选择的web browser，目前支持5种，而且无需安装。收费版有脚本自愈功能。&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;支持的代码&lt;/td&gt;        &lt;td valign="top" width="217"&gt;支持export成多种语言&lt;/td&gt;        &lt;td valign="top" width="236"&gt;只支持Groovy&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;代码查看&lt;/td&gt;        &lt;td valign="top" width="217"&gt;需要其它工具编辑、查看&lt;/td&gt;        &lt;td valign="top" width="236"&gt;界面上可以直接切换显示测试脚本和测试代码并进行编辑&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td valign="top" width="85"&gt;数据驱动&lt;/td&gt;        &lt;td valign="top" width="217"&gt;需要编辑export出的测试代码以支持数据驱动&lt;/td&gt;        &lt;td valign="top" width="236"&gt;支持在界面上创建、编辑、导入数据文件&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;  无代码化的测试工具的兴起从加快软件交付方面来说肯定是很有价值，但从人的角度来说，对于测试人员的职业发展其实会带来冲击，有不少测试人员说：“好不容易培养起来的一点儿编程能力这下也用不上了，真不知道将来我的核心竞争力是什么”。懂业务的测试人员当然也很有价值，但往往不受重视。留给大家的时间也许真的不多了，需要思考一下未来。    &lt;p&gt;      &lt;br /&gt;      &lt;strong&gt;PS：明天有重要消息发布，请关注&lt;/strong&gt;      &lt;img&gt;&lt;/img&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60939-%E4%BB%A3%E7%A0%81-%E6%B5%8B%E8%AF%95-%E8%87%AA%E5%8A%A8%E5%8C%96</guid>
      <pubDate>Thu, 15 Oct 2020 23:21:56 CST</pubDate>
    </item>
    <item>
      <title>微服务下产品集成和集成测试框架流程(200818）</title>
      <link>https://itindex.net/detail/60823-%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E4%BA%A7%E5%93%81-%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95</link>
      <description>&lt;a href="http://album.sina.com.cn/pic/001l8XD7zy7FJDkx8Tva9" target="_blank"&gt;  &lt;img src="http://s10.sinaimg.cn/mw690/001l8XD7zy7FJDkx8Tva9&amp;690"&gt;&lt;/img&gt;&lt;/a&gt;
 &lt;div&gt;  &lt;br /&gt;&lt;/div&gt;
 &lt;div&gt;
  &lt;p&gt;
今天谈下微服务架构下的应用集成和集成测试方面的内容。在微服务架构下，由于传统的的单体应用以及拆分为多个微服务，那么原来单个系统内部的API接口调用以及变成了微服务间的外部接口调用，而且还可能已经由不同的开发团队在开发不同的微服务模块。&lt;/p&gt;
  &lt;p&gt;
在这种情况下如果不能很好的进行产品应用集成和后续集成测试，那么会经常出现类似单元测试问题遗留到集成测试，端到端流程无法测试通过，测试用例和数据反复制作，集成过程中出现问题故障排查困难等诸多问题。&lt;/p&gt;
  &lt;p&gt;
也正是这个原因，今天准备讲下产品集成和集成测试方面的内容，可以看到微服务下的产品集成和集成测试实际和传统组件化开发下的组件集成思路基本是一致的。当然在CMMI三级的时候我们有一个PI的过程域，该过程域也给出了产品集成的核心指导思路。&lt;/p&gt;
  &lt;h1&gt;
产品集成概述&lt;/h1&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p3-tt.byteimg.com/origin/pgc-image/879f91b913d0464eb476c56d20fe2644?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
大型软件产品开发一次可能开发多个新的业务系统，同时一个业务系统本身又包含多个业务模块和组件。只要在前期产品规划中存在子系统和模块的分解，那么后续就一定存在产品集成的动作。&lt;/p&gt;
  &lt;p&gt;
在微服务架构设计下可以看到，我们通过传统单体应用的大拆小，形成了多个松耦合的微服务组件模块。一方面是通过分而治之降低大系统复杂度；另外一方面则是通过分解和接口定义后各模块可以并行开发。&lt;/p&gt;
  &lt;p&gt;
只要架构阶段存在微服务模块拆分动作，那么最终在各模块开发完成后一定存在集成动作。&lt;/p&gt;
  &lt;p&gt;
架构做出一个假设，只要在分解的时候各组件模块按预定的接口契约进行实现，那么后续各个组件一定可以进行集成和组装形成一个完整的产品。所以架构不能仅仅只关心解耦，还必须关心集成和装配。解耦后的东西无法集成，那么分解过程仍然是失败的。&lt;/p&gt;
  &lt;p&gt;
在新的平台+应用，微服务+容器云平台下应用集成可以看到比常规的产品集成更加复杂，对于一个业务应用或组件首先是要考虑和平台层类似消息，缓存等技术服务能力的集成，其次才是考虑组件之间的进一步横向集成。&lt;/p&gt;
  &lt;p&gt;
在这种场景下本身对应用集成的方案，策略和执行等都提出了更高的要求。&lt;/p&gt;
  &lt;p&gt;

应用集成的内容&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p1-tt.byteimg.com/origin/pgc-image/6bdda2f9fb0b416d96198d4c4f97968d?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
对于应用集成的内容主要分为三个方面的内容。&lt;/p&gt;
  &lt;p&gt;

其一：单个微服务模块和平台层能力的持续集成和发布&lt;/p&gt;
  &lt;p&gt;
对于单个微服务模块的持续集成，首先是编写好自动化编译脚本代码，如使用ant工具完成，然后设置定时作业和任务，开发人员按时check
in相关代码。使用CI持续集成工具根据定时任务点在构建环境自动获取最新代码，自动运行ant自动化编译脚本对代码进行编译，编译完成后自动化部署到某个环境。部署完成后运行单元测试自动化脚本对代码进行自动化测试，输出自动化测试结果和报告；如果通过的话测试人员通过QTP进行进一步自动化测试或手工执行一遍冒烟测试脚本，完成本次持续集成。&lt;/p&gt;
  &lt;p&gt;
在持续集成模式下，一方面是可以尽可能早的发现问题，一方面对测试人员随时都可以有一个可进行详细功能性测试的可用环境；其次如果对于多环境，涉及到开发环境测试通过后自动部署集成测试环境，集成测试环境测试通过后自动部署到验收环境等一系列动作。对此我们叫部署流水线模式，实现跨环境的持续集成管理。&lt;/p&gt;
  &lt;p&gt;

其二：微服务两两之间上下游通过API接口服务间的服务集成&lt;/p&gt;
  &lt;p&gt;
对于微服务两两间的横向集成主要是通过微服务提供的API接口服务集成，因此一方面是微服务需要和自身需要消费的提供业务服务能力的上游微服务组件集成获取输入信息和输出；一方面微服务本身也提供相应的API接口服务，需要配合下游的微服务组件进行服务集成和联调。&lt;/p&gt;
  &lt;p&gt;
对于该步骤的集成重点是保证组件上下游之间能够集成通过，业务和数据能够正常流转，其集成的主要依据是组件概要设计中的服务接口设计进行。&lt;/p&gt;
  &lt;p&gt;

其三：基于端到端业务场景的跨多个微服务之间的集成&lt;/p&gt;
  &lt;p&gt;
在微服务两两集成通过后，接着的关键步骤就是根据端到端的业务场景进行跨多个业务组件的应用集成。确保端到端的业务流程能够在多个微服务模块协同下顺利完成。在这个阶段首先是需要保证组件两两集成通过，然后是依据相应的业务流程和业务架构文档，总体应用架构设计文档分析相应的业务场景，准备相应的业务数据进行。&lt;/p&gt;
  &lt;p&gt;
在整个应用集成过程中，微服务间的集成顺序和集成场景是需要重点考虑的问题。&lt;/p&gt;
  &lt;p&gt;

应用集成顺序&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p3-tt.byteimg.com/origin/pgc-image/0f417dcb5f9544a4bdd9f9dbe8b36c67?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
应用集成顺序分为两种模式：&lt;/p&gt;
  &lt;p&gt;

一种是自顶向下的模式进行集成&lt;/p&gt;
  &lt;p&gt;
对于自顶向下方式的集成，首先集成最外层或流程最末端的业务模块，业务模块前置依赖用模拟器(桩)实现。然后继续在每一层按宽度或深度优先，用完全实现模块代替模拟器，并建立下层。以这种方式继续直到所有被测系统中的桩已经实现和测试。在这种模式下可以看到整个集成过程完全是顶层需求驱动进行，集成工作可以较早的开始进行，如果产品集成图是正金字塔结构较容易，模拟器开发较少；反之同理。&lt;/p&gt;
  &lt;p&gt;

一种是自底向上的方式进行集成&lt;/p&gt;
  &lt;p&gt;
对于自底向上集成，首先集成最底层的业务模块，只在底层模块未实现前使用模拟器(桩)。然后继续在实现并测试对上一级模块，这些构件使用已经测试的下级模块。整个系统使用根一级模块测试。对于这种模式模拟器开发较少，同时上次各模块基本可以开始并行测试。这种集成方式最大的风险是如果上次需求变化可能直接影响到最底层。&lt;/p&gt;
  &lt;p&gt;

应用集成的场景&lt;/p&gt;
  &lt;p&gt;
集成场景分析目的是为后续的集成测试用例设计提供依据，集成测试用例要覆盖所有场景。对于场景分析输入主要包括跨模块协同业务流程图、系统需求规格说明书、概要设计说明书等。&lt;/p&gt;
  &lt;p&gt;
对于集成场景分析可以从静态和动态两个层面进行分析：对于静态分析主要分析模块依赖关系，分析某一个服务接口影响到的业务模块具体功能点，可以模块-》模块的矩阵分析方法进行；对于动态分析主要是根据跨系统或模块流程入手，分析跨模块的流程协同，流程协同中所涉及到的所有接口服务。&lt;/p&gt;
  &lt;p&gt;
集成场景的分析将为集成测试用例的设计提供核心输入，要明白，集成测试不是简单的接口测试，接口反映的是跨系统或模块的交互流程，需要通过交互流程的贯通来检验接口本身的正确性。&lt;/p&gt;
  &lt;p&gt;

产品集成和持续集成的关系&lt;/p&gt;
  &lt;p&gt;
产品集成强调是的是把左右的组件最终能够组装和集成起来，形成一个完整的系统。&lt;/p&gt;
  &lt;blockquote&gt;
   &lt;p&gt;
Martin
Fowler对持续集成是一种软件开发实践，即团队开发成员经常集成它们的工作，通常每个成员每天至少集成一次，也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建（包括编译、发布、自动化测试)来验证，从而尽快地发现集成错误。&lt;/p&gt;
&lt;/blockquote&gt;
  &lt;p&gt;
许多团队发现这个过程可以大大减少集成的问题，让团队能够更快的开发内聚的软件。可见持续集成只能算做产品集成的一个子实践。&lt;/p&gt;
  &lt;p&gt;
要明白持续集成只是产品集成的一种方式，不论是开发过程是瀑布模式、增量模式还是迭代模式，都可以采用持续集成的思路。要明白持续集成的一个核心是将整个开发过程透明化，同时将集成工作提前化。尽可能早的暴露问题和风险，同时纠正在前期系统分析和架构设计中的不足。&lt;/p&gt;
  &lt;p&gt;
对于持续集成我们往往会强调每日构建、冒烟测试、自动化测试等内容。&lt;/p&gt;
  &lt;p&gt;
强调开发、测试和生产环境的部署流水线作业。但是要明白对于大型产品集成仍然会包括模块内测试和集成、模块间测试和集成、跨系统间的测试和集成工作。对于单个模块内可以采用每日构建和持续集成策略，但是对于模块间和跨系统我们可以采取分迭代式的集成方式进行集成。&lt;/p&gt;
  &lt;h1&gt;
集成测试流程&lt;/h1&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p6-tt.byteimg.com/origin/pgc-image/b013f66cbd15497e9a0202a76e67661e?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
集成测试是将模块按照设计要求组装起来同时进行测试，主要目标是发现与接口有关的问题。如数据穿过接口时可能丢失；一个模块与另一个模块可能有由于疏忽的问题而造成有害影响；把子功能组合起来可能不产生预期的主功能；个别看起来是可以接受的误差可能积累到不能接受的程度；全程数据结构可能有错误等。&lt;/p&gt;
  &lt;p&gt;
对于集成测试阶段的流程可以参考上图，在验收测试阶段流程和该图类似不再说明。&lt;/p&gt;
  &lt;p&gt;
 &lt;/p&gt;
  &lt;p&gt;
从该图中可以看到作为集成测试的负责方在进行集成测试执行前需要制定集成测试计划，集成测试方案和策略并联同甲方信息化管理部门和开发厂商共同进行计划和方案的评审。在方案评审通过后再开始单个业务组件的集成测试用例的设计，业务组件间的接口和服务测试用例的设计工作。&lt;/p&gt;
  &lt;p&gt;
开发厂商在开发验证环境自身的单元测试通过后将形成一个稳定的可用于集成测试的版本，在这个时候可以提交集成测试申请；而对于配置管理方来说则根据提交的集成测试申请进行待测试版本的提取，并将测试版本部署到集成测试环境，成功部署完成后通知集成测试负责方进行集成测试工作。&lt;/p&gt;
  &lt;p&gt;
集成测试方根据设计好的功能测试用例和接口服务测试用例开始进行业务组件的功能测试和上下游的接口服务测试工作，确保单个业务组件功能正确，和上下游业务组件之间的衔接正确。在这里重点还是集成方案中的集成顺序分析。在集成测试执行完成后输出相应的集成测试结果，如果存在相应的缺陷则打回到开发商，开发商在进行缺陷修复和自测通过后再提交第二轮的集成测试。&lt;/p&gt;
  &lt;p&gt;
在多轮集成测试缺陷全部关闭后，需要在集成测试环境再进行一次回归测试。当回归测试仍然通过后可以开始输出相应的集成测试评估报告并提交评审。在集成测试评估报告和开发商，验收测试商一起评审通过后该业务组件可以开始提交验收测试，并进入详细的验收测试流程。&lt;/p&gt;
  &lt;h1&gt;
集成方案和策略&lt;/h1&gt;
  &lt;p&gt;
可以以多种方式进行集成测试，而下面是三种常用的类别：&lt;/p&gt;
  &lt;p&gt;

第一种方法是由上而下的集成测试方法&lt;/p&gt;
  &lt;p&gt;
首先测试和集成最高级别的模块。这使高级别的逻辑和数据流可以在过程的早期阶段测试，有助于最大限度地减少对驱动程序的需求。但是，对存根
(stub)
的需求使测试管理变得复杂，低级别的实用工具在开发周期中相对较晚的阶段测试。由上而下的集成测试的另一个缺点是不能很好地支持有限功能的早期发布。&lt;/p&gt;
  &lt;p&gt;

第二种方法是由下而上的方法要求&lt;/p&gt;
  &lt;p&gt;
首先测试和集成最低级别的单元。这些单元常被称为实用工具模块。通过使用这种方法，使用工具模块在开发过程的早期阶段测试，最大限度地减少了对存根
(stub)
的需求。但是，不利的方面是对驱动程序的需求使测试管理变得复杂，高级别的逻辑和数据流在晚期测试。与由上而下的方法一样，由下而上的方法也不能很好地支持有限功能的早期发布。&lt;/p&gt;
  &lt;p&gt;

第三种方法（有时也称为伞形方法）测试沿功能性数据和控制流路径进行&lt;/p&gt;
  &lt;p&gt;
首先，函数的输入以上面讨论的由下而上的模式集成。然后，每个函数的输出以由上而下的方式集成。这种方法的主要优点是对有限功能的早期发布的支持程度。它也有助于最大限度地减少对存根
(stub)
和驱动程序的需求。但是，这种方法的潜在缺点非常明显，因为它的系统性可能比其他两种方法低，会导致对回归测试的更大需求。&lt;/p&gt;
  &lt;p&gt;
由于自顶向下的测试方式需要建立大量的技术服务和业务服务模拟器，而且底层的需求变化会对上层的业务组件模块造成较大的影响。因此对于私有云PaaS平台中应用的集成测试最好的方式还是以自底向上的模式进行，在这个过程中为了保证测试工作的尽早开始和并行，可以对少量涉及到技术服务集成的场景采用自顶向下的方式进行集成。&lt;/p&gt;
  &lt;p&gt;
在整个集成测试方案策略中重点是集成依赖关系和集成顺序的分析，具体如下：&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p3-tt.byteimg.com/origin/pgc-image/77a7938e54b544f1b60e09d664bceea1?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
在此图中可以看到，首先需要分析业务组件模块之间的相互依赖关系，每个模块涉及到的前置依赖模块，以及和依赖模块之间需要交互的业务服务接口。基于初步的模块依赖关系分析可以开始考虑业务模块的组装和集成顺序，在集成顺序的分析中可以根据依赖关系按正向和逆向两种方式进行集成测试顺序的分析和梳理。&lt;/p&gt;
  &lt;p&gt;
对于正向分析来说则是当前业务模块测试完成后可以测试哪些下游的业务模块；而对于逆向分析来说则是当前模块的前置依赖模块是哪些；如果需要测试当前模块需要首先测试哪些上游的业务组件模块等。通过这两种方式的梳理基本可以形成一个大框架的组件集成流程图。但是由于业务模块集成本身的复杂性，以上的初步集成方案和策略分析还不足够，最好的方法还是需要进一步结合业务场景尽快跨模块的业务协同分析，具体如下：&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p1-tt.byteimg.com/origin/pgc-image/b788769b0d0f488fac0c98d9aad03fe2?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
结合该图，在集成场景分析中首先是选择需要集成的业务流程，分析该业务流程中的各个业务活动以及这些业务模块之间的交互和协同点。对于这些交互点需要详细的分析业务功能以及该业务功能涉及到的业务服务接口，将这些业务服务接口全部识别出来并进行测试设计和测试数据准备。&lt;/p&gt;
  &lt;p&gt;
基于以上步骤后可以有针对性的对识别出来的业务流程进行跨模块的端到端测试，在这种测试模式下虽然无法保证所有业务模块间的接口全部测试覆盖到，但是可以保证关键的业务流程实现跨模块的业务协同和贯通。&lt;/p&gt;
  &lt;h1&gt;
集成测试执行和评估&lt;/h1&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p6-tt.byteimg.com/origin/pgc-image/61bdfb6770b5475c86cb0e66b5550af3?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
对于集成测试执行设计到冒烟测试、业务组件模块的功能测试、业务组件间接口服务测试、性能测试、易用性测试等多方面的内容，具体可以参考业界标准的测试方法规范体系。对于在测试执行阶段可以简要描述如下：&lt;/p&gt;
  &lt;p&gt;
 &lt;/p&gt;
  &lt;p&gt;
在集成测试执行过程中需要有相应的缺陷管理工具和平台对缺陷进行统一的管理和跟踪。如果从更加全面的工程变更角度来说，则需要有完善的需求变更、缺陷管理、版本管理、测试流程管理、发布管理等一系列的研发过程管理工具平台提供支撑。&lt;/p&gt;
  &lt;p&gt;
对于测试评估则是根据需求设计文档（系统需求，产品集成方案、集成设计文档等），描述经过测试后，哪些组件接口已经实现，哪些组件接口没有实现或存在什么问题，产品对应系统需求是否通过测试，测试执行是否覆盖到所有集成测试用例。同时需要对集成测试执行结果以及因测试不充分而引起的风险进行评估，说明对系统测试的影响。集成测试评估的主要输出包括了：&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;
集成测试清单和测试结果；&lt;/li&gt;
   &lt;li&gt;
集成测试覆盖率分析：接口覆盖率，功能覆盖率；&lt;/li&gt;
   &lt;li&gt;
测试结果统计分析：Bug分析，集成次数和成功率，遗留Bug分析；&lt;/li&gt;
   &lt;li&gt;
测试结果评估；&lt;/li&gt;
   &lt;li&gt;
测试结论和后续改进建议。&lt;/li&gt;
&lt;/ul&gt;
  &lt;h1&gt;
集成测试的其它关键点说明&lt;/h1&gt;
  &lt;p&gt;

通过DevOps实施来实现整个持续集成过程的自动化&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p1-tt.byteimg.com/origin/pgc-image/153008895a2e40f189c9ccf9bb042a86?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
持续集成和集成测试还是有很大区别，持续集成强调的是自动化的编译构建，部署，自动化的冒烟测试，保证开发过程的产出随时都可以构建一个冒烟测试通过的可用版本。而集成测试则涉及到严格的测试策略，测试方案，集成测试顺序，各个集成功能点的覆盖，详细的功能性测试等。集成测试不仅仅是接口测试，更重要的是以接口质量为前提的跨组件功能性测试。&lt;/p&gt;
  &lt;p&gt;
而对于DevOps最佳实践本身就包括了持续集成的过程。&lt;/p&gt;
  &lt;p&gt;
持续集成不仅仅是实现了自动化的编译，打包和部署，自动化的单元测试。更加重要的是实现了环境之间的自动化迁移。&lt;/p&gt;
  &lt;p&gt;
这个能力在我们整个产品集成中相当重要。&lt;/p&gt;
  &lt;p&gt;

可视化的版本部署看板实现可追溯&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p1-tt.byteimg.com/origin/pgc-image/9523b250a0ca4796b3c11884a13ec6cb?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
在产品集成整体策略下，一个核心就是对于后续类似验收测试，生产环境等都是做环境迁移，而部署重新进行版本构建。只有这样才能够保证最终测试人员测试版本的一致性。&lt;/p&gt;
  &lt;p&gt;
同时根据部署流水线方式，需要建立版本部署情况追溯表。比如上图，我们可以很清楚的看到当前在每一个环境各个微服务模块处于什么版本。&lt;/p&gt;
  &lt;p&gt;

集成测试执行顺序&lt;/p&gt;
  &lt;p&gt;
集成测试是整个测试里面的一个完整阶段，不仅仅是包括接口集成测试，而应该是包括了功能冒烟测试，接口测试，功能性测试，非功能性测试等完整内容，具体如下：&lt;/p&gt;
  &lt;div&gt;   &lt;img alt="&amp;#24494;&amp;#26381;&amp;#21153;&amp;#19979;&amp;#20135;&amp;#21697;&amp;#38598;&amp;#25104;&amp;#21644;&amp;#38598;&amp;#25104;&amp;#27979;&amp;#35797;&amp;#26694;&amp;#26550;&amp;#27969;&amp;#31243;" src="https://p3-tt.byteimg.com/origin/pgc-image/0037529e0ff14acda2fc2fde671d94de?from=pc"&gt;&lt;/img&gt;
   &lt;p&gt;
 &lt;/p&gt;
&lt;/div&gt;
  &lt;p&gt;
我一直认为这是集成测试中非常关键的一个内容，集成顺序的确定涉及到前期大量的组件间依赖关系分析，业务功能点和接口对应关系分析等。特别是发展到现在，我们发现很多时候组件间不再是以前单纯的单向依赖关系，由于接口服务注册在总线上，导致多个组件间可以相互依赖，所以前面简单的组件依赖分析已经不适用，替代的方法是基于跨组件的流程协同分析，以核心流程驱动组件间的组装顺序。&lt;/p&gt;
  &lt;p&gt;

同时，对于传统的自顶向下集成和自底向上集成方法往往都不能完全覆盖。很多时候采用的都会是混合集成的策略。一个是为了及早的看到集成的效果我们期望从上向下，但是却需要大量的模拟器和stub桩模块。另外一个是为了减少模拟器，我们从最底层向上集成，但是往往却将风险延迟到最后发现。&lt;/p&gt;
  &lt;br /&gt;&lt;/div&gt; &lt;br /&gt; &lt;img src="http://simg.sinajs.cn/blog7style/images/special/1265.gif"&gt;&lt;/img&gt; &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>微服务架构</category>
      <guid isPermaLink="true">https://itindex.net/detail/60823-%E5%BE%AE%E6%9C%8D%E5%8A%A1-%E4%BA%A7%E5%93%81-%E9%9B%86%E6%88%90%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Tue, 18 Aug 2020 18:48:04 CST</pubDate>
    </item>
    <item>
      <title>kafka压力测试说明书（九） - 简书</title>
      <link>https://itindex.net/detail/60604-kafka-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95-%E8%AF%B4%E6%98%8E%E4%B9%A6</link>
      <description>&lt;h1&gt;1 整体环境说明&lt;/h1&gt;  &lt;h2&gt;1.1 硬件环境&lt;/h2&gt;  &lt;p&gt;1、    &lt;strong&gt;磁盘：&lt;/strong&gt;SATA磁盘2块，磁盘阵列为RAID1&lt;/p&gt;  &lt;p&gt;2、    &lt;strong&gt;CPU****：&lt;/strong&gt;2个4核CPU。具体参数：Intel(R) Xeon(R) CPU E5405 @ 2.00GHz&lt;/p&gt;  &lt;p&gt;3、    &lt;strong&gt;内存：&lt;/strong&gt;8G（8*1G）&lt;/p&gt;  &lt;p&gt;4、    &lt;strong&gt;网卡：&lt;/strong&gt;1000Mb/s&lt;/p&gt;  &lt;h2&gt;1.2 软件环境&lt;/h2&gt;  &lt;p&gt;1、 kafka版本：kafka_2.11-0.11.0.3&lt;/p&gt;  &lt;p&gt;2、 kafka集群数量：3&lt;/p&gt;  &lt;p&gt;3、 zookeeper版本：zookeeper-3.4.12&lt;/p&gt;  &lt;p&gt;4、 zookeeper集群数量：3&lt;/p&gt;  &lt;p&gt;5、 zookeeper使用单独的集群，不使用kafka自带zookeeper&lt;/p&gt;  &lt;h1&gt;2 服务器自身瓶颈测试&lt;/h1&gt;  &lt;p&gt;由于kafka是的吞吐量特别大，所以先考虑集群服务器的自身瓶颈。如磁盘IO瓶颈。由于kafka做的集群所以需要相互传输数据，所以要考虑网卡瓶颈。&lt;/p&gt;  &lt;h2&gt;2.1 测试磁盘IO瓶颈&lt;/h2&gt;  &lt;h3&gt;2.1.1 磁盘IO写入瓶颈&lt;/h3&gt;  &lt;p&gt;1、使用以下命令测试磁盘IO的写入瓶颈&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# sync;time -p bash -c &amp;quot;(dd if=/dev/zero of=test.dd bs=1M count=20000)&amp;quot;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;解释：在当前目录下创建一个test.dd的文件，写入20000个1M的数据。&lt;/p&gt;  &lt;p&gt;2、使用iostat命令监测磁盘io情况。&lt;/p&gt;  &lt;p&gt;使用命令&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# iostat -x 1&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;解释：扩展查看io性能，每1秒钟刷新一次。&lt;/p&gt;  &lt;p&gt;注意：如果没有iostat。请执行yum install sysstat –y命令进行安装iostat命令&lt;/p&gt;  &lt;p&gt;3、结果展示&lt;/p&gt;  &lt;p&gt;（1）磁盘写入IO结果&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# sync;time -p bash -c &amp;quot;(dd if=/dev/zero of=test.dd bs=1M count=20000)&amp;quot;

记录了20000+0 的读入

记录了20000+0 的写出

20971520000字节(21 GB)已复制，221.314 秒，94.8 MB/秒

real 221.67

user 0.01

sys 21.20&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;磁盘写入IO为94.8 MB/秒&lt;/p&gt;  &lt;p&gt;（2）iostat命令结果&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;关注wkB/s和%util两个参数&lt;/p&gt;  &lt;p&gt;wkB/s：每秒写入设备的数据量（单位：KB）&lt;/p&gt;  &lt;p&gt;%util：消耗在I/O请求中的CPU时间百分比（设备带宽利用率）。如果该值接近100%说明设备出现了瓶颈。&lt;/p&gt;  &lt;h3&gt;2.1.2 磁盘IO读取瓶颈&lt;/h3&gt;  &lt;p&gt;1、使用以下命令测试磁盘IO的读取瓶颈&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# hdparm -tT --direct /dev/sda&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;解释：hdparm命令是显示与设定硬盘的参数。-t参数为评估硬盘的读取效率(不经过磁盘cache)。-T参数为评估硬盘的读取效率(经过磁盘cache)&lt;/p&gt;  &lt;p&gt;注意：如果没有hdparm命令可以直接yum –y install hdparm即可&lt;/p&gt;  &lt;p&gt;2、使用iostat命令监测磁盘io情况。&lt;/p&gt;  &lt;p&gt;使用命令&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# iostat -x 1&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;3、结果展示&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# hdparm -tT --direct /dev/sda

/dev/sda:

 Timing O_DIRECT cached reads: 326 MB in 2.00 seconds = 162.83 MB/sec

 Timing O_DIRECT disk reads: 322 MB in 3.01 seconds = 106.88 MB/sec&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;解释：经过磁盘cache的磁盘读取为162.83 MB/sec&lt;/p&gt;  &lt;p&gt;未经过磁盘cache的磁盘读取为106.88 MB/sec&lt;/p&gt;  &lt;h2&gt;2.2 磁盘性能总结&lt;/h2&gt;  &lt;p&gt;以我的服务器SATA磁盘2块，磁盘阵列为RAID1的配置。磁盘写入数据瓶颈为94.8 MB/秒。读取数据瓶颈经过磁盘cache的磁盘读取为162.83 MB/秒，未经过磁盘cache的磁盘读取为106.88 MB/秒。如果kafka集群的写入速度和读取数据的速度达到这个数值，或者iostat的输出结果%util的值接近100%。说明磁盘已经到达一个瓶颈。会影响压测数据的准确性。&lt;/p&gt;  &lt;h2&gt;2.3 网卡性能总结&lt;/h2&gt;  &lt;p&gt;我的网卡是千兆网卡，传输数据可以达到1000Mb/s，由于我们使用的单位都为MB/s。所以把Mb换算成MB。1000Mb/s=125MB/s。也就是说传输熟读到达125MB/s的时候是网卡的瓶颈。会影响压测数据的准确性。&lt;/p&gt;  &lt;h1&gt;3 Kafka测试前期准备&lt;/h1&gt;  &lt;h2&gt;3.1 影响测试结果配置分析&lt;/h2&gt;  &lt;p&gt;Kafka的性能测试主要测试kafka的吞吐量，kafka吞性能为生产者在向kafka传入消息时的写入量，kafka的吐性能为消费者在kafka集群中消费的能力，也就是读取量。&lt;/p&gt;  &lt;h3&gt;3.1.1 Borker相关&lt;/h3&gt;  &lt;p&gt;Kafka的borker是kafka集群的缓存代理，消息中间件处理结点，一个Kafka节点就是一个broker，多个broker可以组成一个Kafka集群。下面是相关broker的参数分析。&lt;/p&gt;  &lt;p&gt;1、num.partiton&lt;/p&gt;  &lt;p&gt;topic物理上的分组，一个topic可以分为多个partition，每个partition是一个有序的队列。&lt;/p&gt;  &lt;p&gt;Partition的数量选取也会直接影响到Kafka集群的吞吐性能。例如我们接口如果开了多个线程去消费kafka的数据，当Partition数量相对于流入流出的数据量显得较少，或由于业务逻辑和Partition数量没有匹配好造成个别Partition读写数据量大，大量的读写请求集中落在一台或几台机器上时就会很影响效率。&lt;/p&gt;  &lt;p&gt;2、Default.replication.factor&lt;/p&gt;  &lt;p&gt;Replication参数为kafka集群副本数。这个参数决定了kafka的高可用性。也决定了kafka的吞吐量。此数据运算和broker个数和broker上的分区数量都有关系。正常broker为3replication设置为1最好。因为3个节点的集群可以宕机一台可以继续工作，而3个replication可以保证宕机两个节点正常工作。所以多replication会造成资源浪费。如果数据不需要持久化和数据不重要并且写入量特别大的话，可以考虑replication为0。&lt;/p&gt;  &lt;p&gt;3、num.network.thread&lt;/p&gt;  &lt;p&gt;用于接收并处理网络请求的线程数，默认为3。其内部实现是采用Selector模型。启动一个线程作为Acceptor来负责建立连接，再配合启动num.network.threads个线程来轮流负责从Sockets里读取请求，一般无需改动，除非上下游并发请求量过大。&lt;/p&gt;  &lt;p&gt;4、写入数据每条大小&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;&amp;apos;{&amp;quot;indexdiy&amp;quot;:&amp;quot;catalina&amp;quot;,&amp;quot;input_type&amp;quot;:&amp;quot;log&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;[2018-09-26 12:30:13,030] [org.apache.tomcat.util.net.NioSelectorPool] [INFO] [Using a shared selector for servlet write/read]&amp;quot;,&amp;quot;offset&amp;quot;:17600578,&amp;quot;project_tag&amp;quot;:&amp;quot;catalina&amp;quot;,&amp;quot;source&amp;quot;:&amp;quot;/opt/tomcat7/logs/catalina.out&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;log&amp;quot;}&amp;apos;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;以上面一条日志为例。此条日志大小为283B。所以我们测试基准为200B和500B。&lt;/p&gt;  &lt;h3&gt;3.1.2 Consumer相关&lt;/h3&gt;  &lt;p&gt;Consumer为kafka的消费者，同一个topic消费者越多越快，但是需要注意的是，消费者的数量不能超过topic的分区数量，因为每个topic的每个分区只能被一个消费者消费，多出来的消费者会无信息可消费。导致资源浪费。&lt;/p&gt;  &lt;h2&gt;3.2 测试命令详解&lt;/h2&gt;  &lt;p&gt;1、创建topic命令&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# ./kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test -7&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;--replication-factor：指定副本个数    &lt;br /&gt;--partitions：指定分区个数    &lt;br /&gt;--topic：指定topic名&lt;/p&gt;  &lt;p&gt;2、查看topic命令&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# ./kafka-topics.sh --zookeeper 10.10.4.11:2181 --list&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;3、查看指定topic的详细内容&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# ./kafka-topics.sh --zookeeper 10.10.4.11:2181 --topic test_property --describe&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;5、 写入数据&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# ./kafka-producer-perf-test.sh --num-records 10000000 --topic test-ref-9 --record-size 500 --throughput 100000 --producer-props bootstrap.servers=10.10.4.11:9092,10.10.4.12:9092,10.10.4.13:9092&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;--num-records：记录的条数    &lt;br /&gt;--topic：指定topic的名字    &lt;br /&gt;--record-size：一条记录大小。    &lt;br /&gt;--throughput：吞吐数量    &lt;br /&gt;--producer-props bootstrap.servers=IP:9092,IP:9092,IP:9092：指定kafka集群&lt;/p&gt;  &lt;p&gt;注意：-- throughput参数为写入数量，如果结果接近此数量，建议*10再测试一次。因为和此结果相近说明kafka没有到达瓶颈。&lt;/p&gt;  &lt;p&gt;6、消费数据&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# ./kafka-consumer-perf-test.sh --messages 10000000 --threads 3 --zookeeper localhost:2181 --num-fetch-threads 3 --topic test-ref-8&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;--messages：指定消费条目数    &lt;br /&gt;--threads：指定线程数    &lt;br /&gt;--num-fetch-threads 3：指定消费人数&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;注意：执行以上命令在kafka****家目录下的/bin****下执行命令&lt;/strong&gt;&lt;/p&gt;  &lt;h1&gt;4 Kafka写入性能测试&lt;/h1&gt;  &lt;p&gt;在测试kafka写入性能测试的时候一边检测系统的cpu使用情况、内存使用情况和磁盘IO情况。&lt;/p&gt;  &lt;h2&gt;4.1 测试kafka的partition参数&lt;/h2&gt;  &lt;h3&gt;4.1.1 创建不同partition的topic并写入数据1000万条数据。&lt;/h3&gt;  &lt;p&gt;1、 创建一个副本，partition分别为1、3、6、12的topic分别为test-0、test-1、test-2、test-3&lt;/p&gt;  &lt;p&gt;2、 向topic内写入1000万条500B的数据，一次写入100万条，replication为1。并且开启另一个窗口使用iostat命令实时监控磁盘IO情况。&lt;/p&gt;  &lt;h3&gt;4.1.2 测试结果&lt;/h3&gt;  &lt;p&gt;1、写入数据如下表&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;3、 磁盘IO情况&lt;/p&gt;  &lt;p&gt;由于磁盘IO瓶颈在94.8 MB/秒得出的数据只有partition为1的情况下在60MB/秒。所以大多数情况下%util处于一个100%的状态。在partition为1的情况下是隔两秒会出现%util值为100%。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;4.2 kafka的partition参数总结&lt;/h2&gt;  &lt;p&gt;由于压力测试没有到达kafka的瓶颈，而是到达了服务器的瓶颈。所以以上数据仅供参考。如想测试更准确的数据。需要性能更好的磁盘来做测试。&lt;/p&gt;  &lt;p&gt;在其他数据相同，而partition不同的时候。结论是partition越多写入速度越快。但是partition数量越多会照成kafka集群可用性越差。所以建议，在实际生产环境。有多少个broker，partition数就为多少。这样可以保证kafka集群的高可用性。可以保证n-1/2个节点宕机而不影响kafka集群使用。&lt;/p&gt;  &lt;h2&gt;4.3 测试Kafka的replication参数&lt;/h2&gt;  &lt;h3&gt;4.3.1 创建不同replication的topic并写入数据1000万条数据。&lt;/h3&gt;  &lt;p&gt;1、创建一个副本，replication分别为2和3的topic为test-4、test-5。和之前创建的test-1。一起做测试&lt;/p&gt;  &lt;p&gt;2、向topic内写入1000万条500B的数据，一次写入100万条，partition为3（因为我的broker是3个）。并且开启另一个窗口使用iostat命令实时监控磁盘IO情况。&lt;/p&gt;  &lt;h3&gt;4.3.2 测试结果&lt;/h3&gt;  &lt;p&gt;1、写入数据如下表&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、磁盘IO情况&lt;/p&gt;  &lt;p&gt;由于磁盘IO瓶颈在94.8 MB/秒得出的数据只有replication为3的情况下在70MB/秒。所以大多数情况下%util处于一个100%的状态。在replication为3的情况下也是一直%util值为100%。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;4.4 kafka的replication参数总结&lt;/h2&gt;  &lt;p&gt;由于压力测试没有到达kafka的瓶颈，而是到达了服务器的瓶颈。所以以上数据仅供参考。如想测试更准确的数据。需要性能更好的磁盘来做测试。&lt;/p&gt;  &lt;p&gt;在其他数据相同，而replication不同的时候。结论是replication越少写入速度越快。但是replication数量越少会照成kafka集群可用性越差。所以建议，在实际生产环境。Kafka集群broker为3的时候replication为1，可以保证一台节点宕机集群可用。其他架构需继续深入研究。&lt;/p&gt;  &lt;h2&gt;4.5 测试Kafka的network.thread参数&lt;/h2&gt;  &lt;p&gt;1、 修改配置文件network.thread的参数为1，重启kafka进行对test-1进行写入测试。&lt;/p&gt;  &lt;p&gt;2、 和之前test-1的数据进行对比。&lt;/p&gt;  &lt;h3&gt;4.5.1 测试结果&lt;/h3&gt;  &lt;p&gt;1、写入数据如下&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、磁盘IO情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;磁盘已经到达瓶颈。&lt;/p&gt;  &lt;h2&gt;4.6 Kafka的network.thread参数总结&lt;/h2&gt;  &lt;p&gt;从结果可看出kafka的network.thread参数越多写入速度越快。但是增加的非常不明显。除非写入速度要求极高的情况，或者机器性能足够好。其他情况建议使用默认值3即可。&lt;/p&gt;  &lt;h2&gt;4.7 测试kafka的单条数据大小参数&lt;/h2&gt;  &lt;p&gt;1、 修改命令--record-size参数。&lt;/p&gt;  &lt;p&gt;2、 同往test-1里写入进行测试&lt;/p&gt;  &lt;h3&gt;4.7.1 测试结果&lt;/h3&gt;  &lt;p&gt;1、写入数据如下&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、磁盘IO情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;磁盘已经到达瓶颈。&lt;/p&gt;  &lt;h2&gt;4.8 Kafka的单条数据大小参数总结&lt;/h2&gt;  &lt;p&gt;从结果显示证明如果写入kafka的数据量单条越小，传输速度越快。正常我们的日志大约在300B每条，最大为500B每条。所以我们按照最大的数据量进行传输来测试写入量。&lt;/p&gt;  &lt;h1&gt;5 Kafka写入数据测试整体总结&lt;/h1&gt;  &lt;p&gt;在kafka写入数据的时候，主要参数在于partition的数量、replication的数量及单条数据的大小。对于线程数对写入速度并不是特别影响。在测试的时候观察cpu使用情况和内存使用情况。Kafka在有写入的时候对于本身的内存要求不大，jvm设置为1G就可以，但是kafka机制是kafka先写入系统页缓存内，所以需要的内存比较大。不建议和使用内存较大的应用部署在一台机器上，如elasticsearch。如果服务器内存较大，建议kafka使用4G左右jvm。Kafka对cpu要求不是特别大。一般两核以上就可以。&lt;/p&gt;  &lt;h1&gt;6 Kafka读取性能测试&lt;/h1&gt;  &lt;p&gt;在测试kafka读取性能测试的时候一边检测系统的cpu使用情况、内存使用情况和磁盘IO情况。&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;注意事项：写入数据后等一段时间再进行测试，因为可能有些数据还在内存中，所以看不出磁盘IO的瓶颈。&lt;/strong&gt;&lt;/p&gt;  &lt;h2&gt;6.1 测试kafka的partition参数&lt;/h2&gt;  &lt;p&gt;1、由于之前已经写入1000万数据，可以直接在test-0、test-1、test-2、test-3的topic 并且Consumer为3、线程为3。直接读取这些数据进行测试。&lt;/p&gt;  &lt;h3&gt;6.1.1 测试结果&lt;/h3&gt;  &lt;p&gt;1、读取数据如下表&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、磁盘IO情况&lt;/p&gt;  &lt;p&gt;磁盘IO有时会到达瓶颈，但是次数不多。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;6.2 Kafka的partition参数总结&lt;/h2&gt;  &lt;p&gt;压测结果证明partition越多速度越快，实际情况我们建议和之前一样。有多少个broker，partition数就为多少。这样可以保证kafka集群的高可用性。可以保证n-1/2个节点宕机而不影响kafka集群使用。详细情况kafka写入测试的partition总结。&lt;/p&gt;  &lt;h2&gt;6.3 测试Kafka的consumer参数&lt;/h2&gt;  &lt;p&gt;以test1为测试topic。分别使用1、3、6个consumer来进行读取测试。&lt;/p&gt;  &lt;h3&gt;6.3.1 测试结果&lt;/h3&gt;  &lt;p&gt;1、写入数据如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、磁盘IO情况&lt;/p&gt;  &lt;p&gt;磁盘IO有时会到达瓶颈，但是次数不多。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;3、 使用软件KafkaOffsetMonitor监控不同consumer的滞留情况lag为滞留信息条目数&lt;/p&gt;  &lt;p&gt;（1）一个consumer三个partition&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（2）三个consumer三个partition&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（3）三个consumer六个partition&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;6.4 kafka的consumer参数总结&lt;/h2&gt;  &lt;p&gt;从测试结果可以看出，consumer这个参数不是越多越好，而是和topic的partition相同时性能最优，如果consumer大于partition的时候，测试开始会报错，内容大意为，有xx个consumer是没有分区可以消费的。这个参数可以根据项目本身去定义。但是不要超过topic的partition数目。但是consumer少会有消息滞留现象。&lt;/p&gt;  &lt;h2&gt;6.5 测试Kafka的线程参数&lt;/h2&gt;  &lt;p&gt;通过test-1 topic进行测试。&lt;/p&gt;  &lt;h3&gt;6.5.1 测试结果&lt;/h3&gt;  &lt;p&gt;1、 写入数据如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、 磁盘IO&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;6.6 kafka的线程参数总结&lt;/h2&gt;  &lt;p&gt;从测试结果来看线程数并不影响kafka的写入速度。&lt;/p&gt;  &lt;h1&gt;7 Kafka读取性能总结&lt;/h1&gt;  &lt;p&gt;Kafka写入性能主要在于partition参数和consumer参数。Partition参数和的具体值可以直接参考写入性能总结，这里不再赘述。Consumer的性能测试来看，只要不多于partition的数量都是可以的。如果broker的数量比较多，建议多设置几个。&lt;/p&gt;  &lt;p&gt;Kafka读取对于replication无关，因为replication不参与读取，只做容灾备份的。对线程数也没那么大的关系。&lt;/p&gt;  &lt;p&gt;读取数据对cpu负载不是特别高，2核以上够用，如果是实时读取数据，对磁盘来说性能要求并不高，因为短时间内，一些数据都是在内存里可以直接取到的。&lt;/p&gt;  &lt;h1&gt;8 Kafka整体性能总结&lt;/h1&gt;  &lt;p&gt;对于ELK集群来说，整体性能还是比较好的，一般影响测试结果都是磁盘的瓶颈造成的。对于磁盘来说用SATA磁盘就可以，因为kafka的写入读取机制都是顺序写入、读取的。SATA顺序读写速度大约在53MB/s和SSD的顺序读取都是差不多的。如果做RAID建议做RAID5。&lt;/p&gt;  &lt;p&gt;Kafka对于CPU和内存要求不是特别大，一般CPU建议在8核以上，内存建议在8G以上。如果服务器性能好kafka的jvm建议设置4G。&lt;/p&gt;  &lt;p&gt;Kafka在我的测试环境下，broker为3的集群情况下。Replication参数为1，partition参数为3，线程数为3，consumer数为3，输入读取的文件大小为500B。整体kafka的写入速度为242665条/秒，传输大小为115.71 MB/秒。读取速度为241390条/秒，传输大小为115MB/秒。&lt;/p&gt;  &lt;p&gt;但是以上数据几乎都是遇到了磁盘IO的瓶颈，数据不是特别准确，希望可以有更好的环境，对kafka进行更全面的测试。&lt;/p&gt;  &lt;h1&gt;最后没有贴上整体数据&lt;/h1&gt;  &lt;h3&gt;吞&lt;/h3&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h3&gt;吐&lt;/h3&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60604-kafka-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95-%E8%AF%B4%E6%98%8E%E4%B9%A6</guid>
      <pubDate>Mon, 18 May 2020 17:47:55 CST</pubDate>
    </item>
    <item>
      <title>Logstash及Elasticsearch 压力测试说明书（十） - 简书</title>
      <link>https://itindex.net/detail/60603-logstash-elasticsearch-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95</link>
      <description>&lt;h1&gt;1 整体环境说明&lt;/h1&gt;  &lt;h2&gt;1.1 硬件环境&lt;/h2&gt;  &lt;p&gt;1、    &lt;strong&gt;磁盘：&lt;/strong&gt;SATA磁盘2块，磁盘阵列为RAID1&lt;/p&gt;  &lt;p&gt;2、    &lt;strong&gt;CPU****：&lt;/strong&gt;2个4核CPU。具体参数：Intel(R) Xeon(R) CPU E5405 @ 2.00GHz&lt;/p&gt;  &lt;p&gt;3、    &lt;strong&gt;内存：&lt;/strong&gt;8G（8*1G）&lt;/p&gt;  &lt;p&gt;4、    &lt;strong&gt;网卡：&lt;/strong&gt;1000Mb/s&lt;/p&gt;  &lt;h2&gt;1.2 软件环境&lt;/h2&gt;  &lt;p&gt;1、 kafka版本：kafka_2.11-0.11.0.3&lt;/p&gt;  &lt;p&gt;2、 kafka集群数量：3&lt;/p&gt;  &lt;p&gt;3、 logstash版本：logstash-5.6.11&lt;/p&gt;  &lt;p&gt;4、 elasticsearch版本：elasticsearch-5.6.11&lt;/p&gt;  &lt;p&gt;5、 elasticsearch集群数量：3&lt;/p&gt;  &lt;h2&gt;1.3 服务器自身瓶颈&lt;/h2&gt;  &lt;p&gt;由kafka性能测试得出结论。服务器SATA磁盘2块，磁盘阵列为RAID1的配置。磁盘写入数据瓶颈为94.8 MB/秒。读取数据瓶颈经过磁盘cache的磁盘读取为162.83 MB/秒，未经过磁盘cache的磁盘读取为106.88 MB/秒。&lt;/p&gt;  &lt;p&gt;网卡瓶颈为1000Mb/s=125MB/s。&lt;/p&gt;  &lt;h1&gt;2 logstash测试前期准备&lt;/h1&gt;  &lt;h2&gt;2.1 影响测试结果配置分析&lt;/h2&gt;  &lt;p&gt;Logstash的性能测试主要测试logstash在kafka集群消费消息的数量，和logstash在对日志进行过滤之后向elasticsearch输出的数量。&lt;/p&gt;  &lt;p&gt;Elasticsearch性能测试主要测试从logstash传输过来的数据进行接收的速度。&lt;/p&gt;  &lt;h3&gt;2.1.1 Logstash分析&lt;/h3&gt;  &lt;p&gt;1、--pipeline-workers参数(-w)&lt;/p&gt;  &lt;p&gt;此参数是filter和output模块的pipeline的线程，默认是cpu核数。&lt;/p&gt;  &lt;p&gt;2、--pipeline-batch-size参数(-b)&lt;/p&gt;  &lt;p&gt;是每个logstash pipeline线程，越大会越消耗JVM内存。是积累多少条日志进行向下传输。默认是125条。&lt;/p&gt;  &lt;p&gt;3、jvm的大小。&lt;/p&gt;  &lt;p&gt;4、多个logstash消费传输的速度。&lt;/p&gt;  &lt;p&gt;注意：因为在ELK中只能使用filter过滤模块，所以不对过滤模块进行测试。&lt;/p&gt;  &lt;h3&gt;2.1.2 Elasticsearch分析&lt;/h3&gt;  &lt;p&gt;1、 提交文件的大小&lt;/p&gt;  &lt;p&gt;也就是logstash--pipeline-batch-size的参数。所以可以一起测试&lt;/p&gt;  &lt;p&gt;2、 jvm的大小&lt;/p&gt;  &lt;h2&gt;2.2 测试相关解释&lt;/h2&gt;  &lt;h3&gt;2.2.1 命令解释&lt;/h3&gt;  &lt;pre&gt;    &lt;code&gt;# ./bin/logstash -w 1 –b 1000 -f ./config/conf/test.conf --path.data=./test_pid/ | pv -abt &amp;gt; /dev/null&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;1、使用pv命令如果请yum直接安装，如果版本过低不支持a选项需要源码安装更新版本的pv命令。&lt;/p&gt;  &lt;p&gt;2、-w为--pipeline-workers&lt;/p&gt;  &lt;p&gt;3、-b为--pipeline-batch-size&lt;/p&gt;  &lt;h3&gt;2.2.2 配置文件解释&lt;/h3&gt;  &lt;p&gt;1、配置文件一&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;# cat test.conf

input {

 generator {

 count =&amp;gt; 10000000

 message =&amp;gt; &amp;apos;{&amp;quot;indexdiy&amp;quot;:&amp;quot;catalina&amp;quot;,&amp;quot;input_type&amp;quot;:&amp;quot;log&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;[2018-09-26 12:30:13,030] [org.apache.tomcat.util.net.NioSelectorPool] [INFO] [Using a shared selector for servlet write/read]&amp;quot;,&amp;quot;offset&amp;quot;:17600578,&amp;quot;project_tag&amp;quot;:&amp;quot;catalina&amp;quot;,&amp;quot;source&amp;quot;:&amp;quot;/opt/tomcat7/logs/catalina.out&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;log&amp;quot;}&amp;apos;

 }

}

filter {

 json {

 source =&amp;gt; &amp;quot;message&amp;quot;

 }

 mutate {

 gsub =&amp;gt; [&amp;quot;message&amp;quot;, &amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;]

 }

 grok {

 match =&amp;gt; { &amp;quot;message&amp;quot; =&amp;gt; &amp;quot;\[%{TIMESTAMP_ISO8601:timestamp}\] \[%{JAVACLASS:class}\] \[%{LOGLEVEL:level}\] \[%{GREEDYDATA:logmessage}&amp;quot;}

 }

 date {

 match =&amp;gt; [&amp;quot;timestamp&amp;quot;, &amp;quot;yyyy-MM-dd HH:mm:ss,SSS&amp;quot;]

 target =&amp;gt; &amp;quot;@timestamp&amp;quot;

 }

}

output {

 stdout {

 codec =&amp;gt; dots

 }

 elasticsearch {

 hosts =&amp;gt; [&amp;quot;10.10.4.11:9200&amp;quot;,&amp;quot;10.10.4.12:9200&amp;quot;,&amp;quot;10.10.4.13:9200&amp;quot;]

 index =&amp;gt; &amp;quot;test12_1&amp;quot;

 }

 kafka {

 bootstrap_servers =&amp;gt; &amp;quot;10.10.4.11:9092,10.10.4.12:9092,10.10.4.13:9092&amp;quot;

 topic_id =&amp;gt; &amp;quot;yace1013_1&amp;quot;

 }

}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;配置文件解释：&lt;/p&gt;  &lt;p&gt;Generator模块为自动创建消息模块。&lt;/p&gt;  &lt;p&gt;Count为创建多少条消息。&lt;/p&gt;  &lt;p&gt;Message为消息内容，此消息内容为tomcat的一条日志文件，大约300B。&lt;/p&gt;  &lt;p&gt;Filter为过滤模块。&lt;/p&gt;  &lt;p&gt;Codec为把每条消息都输出一个点（·），一个点的大小为1B&lt;/p&gt;  &lt;p&gt;Elasticsearch为输出到es集群。&lt;/p&gt;  &lt;p&gt;Kafka是输出到kafka集群，为了后期在kafka中消费。&lt;/p&gt;  &lt;p&gt;3、 配置文件二&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;input {

 kafka {

 bootstrap_servers =&amp;gt; &amp;quot;10.10.4.11:9092,10.10.4.12:9092,10.10.4.13:9092&amp;quot;

 topics =&amp;gt; &amp;quot;catalina&amp;quot;

 group_id =&amp;gt; &amp;quot;2s&amp;quot;

 }

}

filter {

 json {

 source =&amp;gt; &amp;quot;message&amp;quot;

 }

 mutate {

 gsub =&amp;gt; [&amp;quot;message&amp;quot;, &amp;quot;\n&amp;quot;, &amp;quot;&amp;quot;]

 }

 grok {

 match =&amp;gt; { &amp;quot;message&amp;quot; =&amp;gt; &amp;quot;\[%{TIMESTAMP_ISO8601:timestamp}\] \[%{JAVACLASS:class}\] \[%{LOGLEVEL:level}\] \[%{GREEDYDATA:logmessage}&amp;quot;}

 }

 date {

 match =&amp;gt; [&amp;quot;timestamp&amp;quot;, &amp;quot;yyyy-MM-dd HH:mm:ss,SSS&amp;quot;]

 target =&amp;gt; &amp;quot;@timestamp&amp;quot;

 }

}

output {

 stdout {

 codec =&amp;gt; dots

 }

}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;配置文件解释：&lt;/p&gt;  &lt;p&gt;Input为在kafka里消费消费数据。要先使用脚本在catalina.out里写上很多数据。&lt;/p&gt;  &lt;p&gt;注意：要先启动下消费脚本在停止，这样logstash才可以订阅到kafka的topic。不让会因为没有订阅topic而不能消费。&lt;/p&gt;  &lt;p&gt;其他不在赘述&lt;/p&gt;  &lt;h3&gt;2.2.3 输出结果解释&lt;/h3&gt;  &lt;p&gt;如果使用codec =&amp;gt; dots（默认都使用方便计算），就是把每一条日志都转换为点（·）一个点是1Byte，如果结果为[5.53kiB/s]，就是每秒钟5530条/秒。&lt;/p&gt;  &lt;p&gt;注意：不同测试环境需要相应的注释一些模块&lt;/p&gt;  &lt;h2&gt;2.3 注意情况&lt;/h2&gt;  &lt;p&gt;1、 在测试的时候如果集群搭建在同一台服务服务器上，需要停止其他的服务，比如说，测试logstash消费kafka的能力，要把elasticsearch关掉，因为elasticsearch很消耗内存。会造成不准确的结果。&lt;/p&gt;  &lt;p&gt;2、 在测试的时候时刻观察着服务器自身的瓶颈，如IO瓶颈，内存瓶颈、CPU瓶颈等。&lt;/p&gt;  &lt;h1&gt;3 Logstash本身性能测试&lt;/h1&gt;  &lt;h2&gt;3.1 测试–w参数&lt;/h2&gt;  &lt;p&gt;1、结果如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、CPU负载如下：&lt;/p&gt;  &lt;p&gt;（1）-w为1&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（2）-w为2&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（3）-w为4&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（4）w为6&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（5）w为8&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;3、 磁盘IO使用情况（由于直接在输出了，并没有使用磁盘）&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;4、内存使用情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;3.2 -w参数总结&lt;/h2&gt;  &lt;p&gt;-w参数为--pipeline-workers，从结果可以看出-w参数根据自身cpu核数相关，实验服务器cpu总核数为8核，所以-w为6的时候性能最高，-w为8的时候性能反而会下降。cpu的负载也跟-w参数相关。服务器内存占用率也是比较大的。所以测试并没有测出logstash本身的性能所在，被cpu的核数限制。只有更多核数的服务器才能测试出logstash的准确性能。&lt;/p&gt;  &lt;h2&gt;3.3 –b参数测试&lt;/h2&gt;  &lt;p&gt;1、 结果如下表&lt;/p&gt;  &lt;p&gt;由于-w选项为6的时候是服务器的一个瓶颈，所以测试-b的时候性能会下降，是服务器的原因，所以我们以-w为4进行测试。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、 cpu负载&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;3、内存使用情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;3.4 -b参数总结&lt;/h2&gt;  &lt;p&gt;从结果可知，-b选项是依赖服务器本身性能的，没有一个确定的值。在测试服务区配置，-b的值为500的时候是最优的。这个最优值需要根据实际情况进行测试。&lt;/p&gt;  &lt;h2&gt;3.5 Jvm大小&lt;/h2&gt;  &lt;p&gt;修改jvm.options配置文件&lt;/p&gt;  &lt;p&gt;1、 结果如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、cpu负载&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、 内存使用情况&lt;/p&gt;  &lt;p&gt;（1）jvm为1G&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;（2）jvm为2G&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;3.6 Jvm大小总结&lt;/h2&gt;  &lt;p&gt;Jvm参数对logstash的性能特别小，按照正常来说，一般设置2-4G为最佳。如果内存特别大可以考虑再增加。&lt;/p&gt;  &lt;h1&gt;4 Logstash消费kafka性能测试&lt;/h1&gt;  &lt;p&gt;由于机器数量有限，我在kafka一个节点上部署的logstash，由于一起工作可能会影响测试结果&lt;/p&gt;  &lt;h2&gt;4.1 Logstash数量&lt;/h2&gt;  &lt;p&gt;1、结果如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;其中logstash为2的时候，13.1kiB/s数据为kafka节点上的logstash。&lt;/p&gt;  &lt;p&gt;3、 CPU负载&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;4.2 Logstash数量总结&lt;/h2&gt;  &lt;p&gt;如果多个logstash在kafka里消费不会影响消费的速度，这样可以保证在日志特别多的时候可以横向扩展logstash来提高性能。从而解决logstash的瓶颈问题。但是要注意消费者组的分配，如果logstash的数量少于kafka的partition，可以随便分配。如果logstash的数量多于kafka的partition。需要分不同的消费者组去消费。不然会有消费者无法消费到数据。详细请看kafka性能测试文档。&lt;/p&gt;  &lt;h1&gt;5 Elasticsearch性能测试&lt;/h1&gt;  &lt;p&gt;Elasticsearch性能测试主要是测试自身性能，也就是logstash在往kafka里写入数据时候的速度，和自身的服务器瓶颈去观察elasticsearch的瓶颈所在。&lt;/p&gt;  &lt;h2&gt;5.1 Logstash向elasticsearch写入数据测试&lt;/h2&gt;  &lt;p&gt;由于机器数量问题，在elasticsearch节点上开启一个logstash，由于一起工作可能会影响测试结果。&lt;/p&gt;  &lt;h3&gt;5.1.1 logstash的数量&lt;/h3&gt;  &lt;p&gt;1、 结果如下表&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;其中6.04kiB/s的数据来自和elasticsearch节点在一起的logstash数据。&lt;/p&gt;  &lt;p&gt;2、 CPU负载情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;3、 内存使用情况&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h3&gt;5.1.2 Jvm大小总结&lt;/h3&gt;  &lt;p&gt;测试过jvm为2G和4G性能测距几乎没有，由于elasticsearch运行消耗很大的性能。所以在这里不详细说明，有更好配置的机器在进行测试。&lt;/p&gt;  &lt;h3&gt;5.1.3 elasticsearch自身测试&lt;/h3&gt;  &lt;p&gt;使用bigdisk插件对elasticsearch进行监控，发现只要elasticsearch启动，机器内存就被消耗了6.8G。只要有写入操作，内存就会到达7.5G。所以测试机器性能太差，需要更大的内存的服务器进行测试。&lt;/p&gt;  &lt;p&gt;1、 启动未做任何操作&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;i&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;2、写入数据&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;5.2 Logstash向elasticsearch写入数据总结&lt;/h2&gt;  &lt;p&gt;由于logstash特别消耗cpu，elasticsearch特别消耗内存，而在两个logstash传入elasticsearch的时候logstash和elasticsearch在同一台服务器上，所以测试数据并不是特别准确。需要性能更好的服务器来进行测试。&lt;/p&gt;  &lt;h1&gt;6 Logstash和elasticsearch整体总结&lt;/h1&gt;  &lt;h2&gt;6.1 Logstash总结&lt;/h2&gt;  &lt;p&gt;在测试环境机器硬件环境下。在logstash消费kafka的情况下单节点logstash的速度为14000条/秒，自身消费的速度为20000条/秒，输出到elasticsearch的速度为7430条/秒。&lt;/p&gt;  &lt;h3&gt;6.1.1 Logstash的性能&lt;/h3&gt;  &lt;p&gt;1、-b选项也就是--pipeline-batch-size参数，这是一个不确定的参数，越大会越消耗JVM内存。是积累多少条日志进行向下传输。这个可以根据实际情况进行测试之后得出结论。&lt;/p&gt;  &lt;p&gt;2、-w选项也就是--pipeline-workers参数，此参数是filter和output模块的pipeline的线程，理论来说越大越好，但是不能超过cpu的总核数，因为-w选项越大，消耗cpu性能也就越多。所以实际情况下根据服务器的总核心数进行设置。&lt;/p&gt;  &lt;p&gt;3、jvm的大小对logstash影响并不是特别大，在生产环境建议给2-4G。&lt;/p&gt;  &lt;h2&gt;6.2 Elasticsearch总结&lt;/h2&gt;  &lt;p&gt;Elasticsearch消耗内存严重，所以测试的数据并没有测试出elasticsearch的性能瓶颈所在点。&lt;/p&gt;  &lt;h3&gt;6.2.1 Elasticsearch性能&lt;/h3&gt;  &lt;p&gt;由于elasticsearch类似数据库，并且写入量特别大，一般在elk上不会成为瓶颈。主要优化还在于索引方面。&lt;/p&gt;  &lt;p&gt;1、 bulk的参数，此参数相当于数据库里的bash操作。引入批量操作bulk，可以提高工作效率。但是由于机器性能无法测试。&lt;/p&gt;  &lt;p&gt;2、 内存要求很高，elasticsearch要求内存很高，一般建议在32G以上。&lt;/p&gt;  &lt;p&gt;3、 在内存要求满足的情况下，磁盘会成为瓶颈。官方建议使用SSD。&lt;/p&gt;  &lt;p&gt;注意：以上性能并没有实际测试，只是通过官网或者其他来源得出的结论，只限参考。&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60603-logstash-elasticsearch-%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Mon, 18 May 2020 17:47:40 CST</pubDate>
    </item>
    <item>
      <title>MySQL云原生方案在携程开发测试场景中的实践</title>
      <link>https://itindex.net/detail/60570-mysql-%E6%90%BA%E7%A8%8B-%E5%BC%80%E5%8F%91</link>
      <description>&lt;h2&gt;一、背景与使用场景&lt;/h2&gt;
 &lt;p&gt;随着Kubernetes平台在容器云计算领域的一统天下，云原生 (Cloud Native) 一词也被提的越来越频繁。各类应用纷纷走上了容器化、云原生化的道路，无状态服务应用在Kubernetes平台上的运行，已经得到了大规模生产级别的实践认可。&lt;/p&gt;
 &lt;p&gt;相比之下，有状态应用就没有那么顺利了，特别是那些十分重要却又&amp;quot;历史悠久&amp;quot;、不是按照分布式架构理念设计的有状态服务，尤其困难。MySQL就是其中的代表，为此我们做了诸多尝试，从一开始的MySQL单实例容器化使用本地存储，到计算存储分离的方案，走了一些弯路。最终在开发测试场景下找了一个合适的切入点，实现了一套计算和存储分离，以Kubernetes Operator为核心，以CEPH RBD为后端存储，以数据库版本化管理为特性的可行方案。&lt;/p&gt;
 &lt;p&gt;我们典型的使用场景是这样的：测试人员需要构造一个生产环境批量订单数据异常的测试场景， 他使用安全工具从生产环境拉取大量脱敏后的数据写入测试数据库，但只运行一次测试用例， 数据库就&amp;quot;脏了&amp;quot;。特别是每次上新功能还要回归测试一次这种场景，又要重复耗时在构造新数据库，真的是“构造2小时，运行5分钟”。&lt;/p&gt;
 &lt;p&gt;而有了这一套完整的MySQL实例服务后，可以快速启动任意版本的数据库实例，前面所述的痛点就彻底消失了。同时有了MySQL实例服务，对CPU 内存资源的使用也可以节省一大笔，毕竟大量的测试数据库都只要以快照的形式存储在集群中即可，实际使用时可以在一两分钟内快速启动。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/b5/01/b545404eb47990fbd1a205fe7c3c7101.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;二、可行性方案分析和性能评估&lt;/h2&gt;
 &lt;p&gt;首先要解决的是计算和存储分离的问题，如果使用容器宿主机本地磁盘存储的话，MySQL实例必须和宿主机绑定，这就丧失了资源的灵活性，而且使用本地存储， 对于磁盘容量的规划会是个不小的问题。&lt;/p&gt;
 &lt;p&gt;我们团队早在2015就开始使用CEPH存储服务，主要是对象存储和块存储，运维经验和集群稳定性方面相对有保证。结合这一实际情况，我们选择使用了CEPH块存储服务作为MySQL容器实例的存储。&lt;/p&gt;
 &lt;p&gt;另一个考量则是受益于Kubernetes这个强大的平台基座，社区已经定义好了容器存储接口 （CSI），且实现了CSI driver for CEPH (  &lt;a href="https://github.com/ceph/ceph-csi"&gt;https://github.com/ceph/ceph-csi&lt;/a&gt;)，其中RBD 部分早已GA，还有提供了snapshot，resize等功能，完全满足我们的使用场景。&lt;/p&gt;
 &lt;p&gt;为了验证MySQL实例后端挂载CEPH块存储服务能否满足开发测试环境的数据库基本使用需求，我们基于已有的硬件情况， 做了两个场景的性能压测。主要是对比使用本地SAS磁盘存储的MySQL实例和使用CEPHRBD的MySQL实例，在性能方面是否有明显差异。其次则是测试MySQL实例后端挂载CEPH RDB存储的性能上限。&lt;/p&gt;
 &lt;p&gt;基本硬件信息如下 ：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;&lt;/th&gt;
   &lt;th&gt;使用本地磁盘的容器宿主机&lt;/th&gt;
   &lt;th&gt;CEPH 集群所用物理机&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;CPU&lt;/td&gt;
   &lt;td&gt;2Socket  / 32Core / 64Thread Intel® Xeon® CPU E5-2650 v3 @ 2。30GHz&lt;/td&gt;
   &lt;td&gt;2Socket  / 32Core / 64Thread Intel®  Xeon® Gold 6130 CPU @ 2。10GHz&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Memory&lt;/td&gt;
   &lt;td&gt;188GB&lt;/td&gt;
   &lt;td&gt;256GB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Disk&lt;/td&gt;
   &lt;td&gt;2*300G，10K(1)    &lt;br /&gt;8*900G，10K(10)&lt;/td&gt;
   &lt;td&gt;2*240G，SSD(10)    &lt;br /&gt;12*8T，7。2K(JBOD)&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;构造了两个测试场景，使用sysbench执行压测：&lt;/p&gt;
 &lt;p&gt;sysbench参数如下：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;参数名&lt;/th&gt;
   &lt;th&gt;参数值&lt;/th&gt;
   &lt;th&gt;备注&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;oltp-tables-count&lt;/td&gt;
   &lt;td&gt;64&lt;/td&gt;
   &lt;td&gt;测试表的个数64张&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;oltp-table-size&lt;/td&gt;
   &lt;td&gt;1000000&lt;/td&gt;
   &lt;td&gt;单表数据量100W&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;num-threads&lt;/td&gt;
   &lt;td&gt;[8，16，32，64，128，256]&lt;/td&gt;
   &lt;td&gt;测试线程数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;times of test&lt;/td&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;每个线程下的测试次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;warmup time&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
   &lt;td&gt;预热时间，避免冷数据对测试结果的影响&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;max-time&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
   &lt;td&gt;每次压测耗时120s，重复3次&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;测试场景A：分别压测MySQL docker with CEPH RBD 和MySQL docker with local disk，并发数threads从低到高，8→256，对比QPS。&lt;/p&gt;
 &lt;p&gt;测试场景B：同时压测五个MySQL docker with CEPH RBD，保持并发数恒定在256，观察CEPH集群IOPS和最终MySQL QPS。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/38/f1/38974f472fc280097d638335c440b3f1.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;测试场景A结果：&lt;/p&gt;
 &lt;p&gt;OLTP模式压测MySQL，对于磁盘主要是随机读写操作，CEPH RBD使用了SSD作为缓存盘，随机写速度约110MB/s，而本地机械磁盘随机写速度只有48.6MB/s，所以最终性能指标QPS，使用了CEPH RBD的容器实例反而更好。&lt;/p&gt;
 &lt;p&gt;测试场景B结果：&lt;/p&gt;
 &lt;p&gt;压测CEPH RBD集群的磁盘IO上限，约算测试环境的集群能提供的QPS上限为80K。&lt;/p&gt;
 &lt;p&gt;结论是在开发测试环境使用CEPH RBD为后端存储的MySQL实例服务，不会比使用本地磁盘更差，可以满足应用功能测试的性能需求。&lt;/p&gt;
 &lt;h2&gt;三、MySQL容器化实例方案及实现细节&lt;/h2&gt;
 &lt;p&gt;介绍一下这套方案的简单架构设计和基本工作原理，如下图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/06/dd/068f819637f10f0765b094ab6eee33dd.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;所有相关服务都部署在Kubernetes集群上，这里只重点描述我们开发的MySQL-Operator和自定义资源CRD。关于CSI driver 以及provisioner，attacher， snapshotter等组件都是使用原生官方镜像，在这里不做详细表述，可以参考文档(  &lt;a href="https://kubernetes-csi.github.io/docs/"&gt;https://kubernetes-csi.github.io/docs/&lt;/a&gt;)。&lt;/p&gt;
 &lt;p&gt;MySQL-Operator作为自定义的控制器，管理两种自定义资源(CRD)，通过Kube-api为上层的PAAS平台和CI等系统提供MySQL实例服务。两个CRD分别是MySQLInstance和DatabaseSnapshot。其中MySQLInstance是基于StatefulSet的一层封装，添加了一些metadata，MySQL-Operator只需要根据MySQLInstance的声明来创建对应的StatefulSet和PVC即可， 所以MySQLInstance暴露出来的spec并不多，大致如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/fa/14/fa1fe444d7bee736eda47a491e099f14.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;根据spec.init的类型，MySQLInstance既可以是基于生产数据库Schema生成的空数据库实例，也可以是基于已有的DatabaseSnapshot生成的带有基准数据的实例。&lt;/p&gt;
 &lt;p&gt;在创建的过程中，MySQL-Operator会为这个MySQLInstance申请域名，同步账户密码以及Schema等。一个MySQLInstance的整个生命周期在有限的七个状态之间跳转。需要特别提一下Paused状态，当基于该实例的DatabaseSnapshot创建时，MySQLInstance会进入Paused状态。状态机如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/4d/ff/4dca6ce6bb2ee76e58f80a74209909ff.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;另一个CRD，DatabaseSnapshot则是基于VolumeSnapshot的封装，其中VolumeSnapshot是Kubernetes官方定义的持久卷快照声明(  &lt;a href="https://kubernetes.io/zh/docs/concepts/storage/volume-snapshots/"&gt;https://kubernetes.io/zh/docs/concepts/storage/volume-snapshots/&lt;/a&gt;)。MySQL-Operator根据它的声明来关联MySQLInstance和PVC即可。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/48/ed/483501362416893cbc6a2b4300ab34ed.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;由于CEPH RBD 的读写独占模式 RWO(read write once)， 我们为DatabaseSnapshot定义了两个常态InUse和Ready。简单来讲就是一个数据库快照同一时间只允许一个数据库实例使用，并且DatabaseSnapshot在创建过程中需要暂停对应的MySQLInstance，状态机如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://static001.infoq.cn/resource/image/ad/65/adefecb768576fe1da45c333f8e7cf65.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;四、小结与展望&lt;/h2&gt;
 &lt;p&gt;在有了MySQLIntance服务之后，数据库的版本管理变得和代码版本管理一样灵活。特别是重复构造测试数据的场景，节省了大量的时间和管理成本。另外用户也不再需要长期占用计算资源，仅在有使用需求时即可快速创建 MySQLInstance，有效提高了整体容器宿主机资源的使用率。除此之外，上层CI/CD平台服务也可以通过Kube API调用的方式来管理这两种CRD，进一步提升测试自动化程度。&lt;/p&gt;
 &lt;p&gt;一般来说，应用云原生化完成后最重要的是获得两个能力：弹性和分布式，目前我们的这套方案落地于Kubernetes平台，释放出了一部分平台计算和存储的弹性，让用户对于数据库实例有了更多的选择和灵活管理的能力。&lt;/p&gt;
 &lt;p&gt;何谓云原生(Cloud Native)， 字面上早已经有了明确的定义(  &lt;a href="https://github.com/cncf/toc/blob/master/DEFINITION.md"&gt;https://github.com/cncf/toc/blob/master/DEFINITION.md&lt;/a&gt;)，但是在工程实践中，基于Kubernetes这个巨大的平台，仍然有大量的宝藏等着我们去持续探索挖掘。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;作者介绍&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;Alex，专注于云计算领域数年，目前主要从事容器云平台的建设，推进各类基础设施服务的云原生化。&lt;/p&gt;
 &lt;p&gt;小石川，目前主要从事容器云平台监控系统建设，对分布式、性能以及优化感兴趣。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;本文转载自公众号携程技术（ID：ctriptech）。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;原文链接&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&amp;mid=2697269592&amp;idx=2&amp;sn=2ebc6e51909422ae573fafcf943500c7&amp;chksm=8376ee6cb401677a803bd57cb8e3859ee0a635ce965449d7cf3b81b6a2b7cb1f14b973580da2&amp;scene=27#wechat_redirect"&gt;https://mp.weixin.qq.com/s?__biz=MjM5MDI3MjA5MQ==&amp;amp;mid=2697269592&amp;amp;idx=2&amp;amp;sn=2ebc6e51909422ae573fafcf943500c7&amp;amp;chksm=8376ee6cb401677a803bd57cb8e3859ee0a635ce965449d7cf3b81b6a2b7cb1f14b973580da2&amp;amp;scene=27#wechat_redirect&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60570-mysql-%E6%90%BA%E7%A8%8B-%E5%BC%80%E5%8F%91</guid>
      <pubDate>Tue, 05 May 2020 14:05:00 CST</pubDate>
    </item>
  </channel>
</rss>

