<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AREX Test</title>
    <description>The latest articles on DEV Community by AREX Test (@arex_test).</description>
    <link>https://dev.to/arex_test</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F7874%2F07b60583-0e05-4bae-94b6-398c6b08b7ba.png</url>
      <title>DEV Community: AREX Test</title>
      <link>https://dev.to/arex_test</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arex_test"/>
    <language>en</language>
    <item>
      <title>AREX or GoReplay: Choosing the Right Tool for Effective Traffic Replay</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 21 Dec 2023 08:53:41 +0000</pubDate>
      <link>https://dev.to/arex_test/arex-or-goreplay-choosing-the-right-tool-for-effective-traffic-replay-1pmp</link>
      <guid>https://dev.to/arex_test/arex-or-goreplay-choosing-the-right-tool-for-effective-traffic-replay-1pmp</guid>
      <description>&lt;p&gt;As software applications grow more complex, encompassing elements like microservices, containers, and intricate connections, predicting and manually scripting test cases for every conceivable scenario has turned into a formidable challenge. The conventional methodology, which involves the creation and validation of test cases, demands significant manual labor and the upkeep of extensive script libraries. This traditional approach is increasingly seen as labor-intensive and less feasible in the dynamic landscape of modern software development.&lt;/p&gt;

&lt;p&gt;Nevertheless, the advent of Production traffic replay has revolutionized the process of testing and validating applications. This innovative approach facilitates the replication of actual production traffic, enabling the realistic simulation and thorough exercise of real-world scenarios. By capturing and replaying network traffic, this method offers a streamlined path to accelerate the testing process. More importantly, it instills greater confidence in the modifications applied to the application, ensuring they perform robustly in live environments.&lt;/p&gt;

&lt;p&gt;This approach offers several distinct advantages compared to conventional test case generation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Produces tests that are more representative of real-world scenarios.&lt;/li&gt;
&lt;li&gt;Significantly lessens the cognitive effort needed to devise test cases.&lt;/li&gt;
&lt;li&gt;Uncovers potential bugs that might remain undetected in a controlled testing environment.&lt;/li&gt;
&lt;li&gt;Utilizes a diverse range of data for more comprehensive testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Several tools capable of capturing production traffic, such as GoReplay and AREX, are already available in the market. Both AREX and GoReplay highlight this feature prominently as a unique selling point. Therefore, this comparison will primarily focus on how each product leverages production traffic capture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traffic replay or traffic mirroring?
&lt;/h3&gt;

&lt;p&gt;Gor implements production traffic replay through a technique known as traffic mirroring. This method involves shadowing live user traffic and redirecting it to a test environment. This can be done either in real-time or asynchronously using systems like Apache Kafka, file systems, and other such platforms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dc2in3ii4rw22rg04u2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dc2in3ii4rw22rg04u2.png" alt="Image description" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AREX is a testing platform for traffic replay, which involves recording the live traffic from online environment and replaying it to the test environment for API testing. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftchj0a047dnrjg5sx3h0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftchj0a047dnrjg5sx3h0.png" alt="Image description" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The traffic replay mechanism employed by AREX, while similar to traffic mirroring, presents some key distinctions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistence of Traffic Data:&lt;/strong&gt; Unlike traffic mirroring, which doesn’t store the captured traffic, AREX's approach allows for the retention and replay of traffic at any desired moment. This feature is particularly beneficial for testing scenarios like Black Friday traffic surges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Validation:&lt;/strong&gt; Traffic mirroring lacks built-in application validation, requiring developers to write their own, often brittle and single-use, validation checks. In contrast, AREX addresses this gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Sanitization:&lt;/strong&gt; Raw data captured through traffic mirroring typically requires sanitization to address issues such as authentication, timestamp adjustments, and the protection of sensitive information. AREX considers this essential step, ensuring the suitability of data for use in lower-level environments, a process often overlooked in basic traffic mirroring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ease of getting started
&lt;/h3&gt;

&lt;p&gt;GoReplay is renowned for its straightforward setup process. Implementation is as simple as downloading a binary, involving fewer initial steps than AREX. However, when delving deeper into its usage, it becomes apparent that GoReplay lacks certain functionalities found in more specialized testing tools. This is especially noticeable when compared to AREX, which is purpose-built for API testing and offers a more comprehensive set of features.&lt;/p&gt;

&lt;p&gt;A significant distinction lies in GoReplay's lack of a WebUI, necessitating interaction with the tool predominantly via the terminal. This contrasts sharply with AREX, which boasts a WebUI, offering a more intuitive and user-friendly interface. This graphical interface simplifies the management and configuration of the tool, enhancing the overall user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Reliability&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the assessment of traffic replay solutions, reliability — the consistent replay of traffic — is a critical factor. It's crucial to understand that GoReplay operates differently from a proxy. Instead, it implements a system-level hook that records all HTTP socket traffic occurring on a specified port. Once this traffic is captured, replaying it involves using a command that is almost identical to the one used for recording:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// capture traffic 
gor --input-raw :8000 --output-file ./requests.gor 

// replay the traffic 
gor --input-raw :80 --output-http "http://target_server:8080"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While GoReplay natively integrates with Kafka for data storage, this setup presents notable limitations. Specifically, when using Kafka, data must be recorded and replayed simultaneously, which can compromise reliability.&lt;/p&gt;

&lt;p&gt;In contrast, AREX emerges as a more comprehensive tool, offering seamless integration of traffic recording and playback with automated testing. It supports the replay of recorded data at any time and multiple times, enhancing both flexibility and reliability in its use.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Data sensitivity&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Capturing production traffic often involves dealing with sensitive user information, necessitating stringent measures for data anonymization or removal during the capture process. In the case of Gor, its traffic mirroring approach results in the collection of raw data, which initially lacks sanitization, potentially raising security concerns during its operation.&lt;/p&gt;

&lt;p&gt;Conversely, for users who place a high priority on data security, AREX stands out with its strong commitment to storing only desensitized data. This approach substantially increases the safety and privacy protection of the captured traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  API mocking
&lt;/h3&gt;

&lt;p&gt;AREX distinguishes itself from GoReplay by offering the valuable ability to modify recorded data. This feature grants users the flexibility to tailor and adjust captured traffic to suit specific testing requirements. Such a capability is crucial for simulating diverse scenarios and edge cases, thereby elevating the comprehensiveness of your testing strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jckabh-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://doc.arextest.com/img/feature-collapse/mock_data_editable-light-en.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jckabh-b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://doc.arextest.com/img/feature-collapse/mock_data_editable-light-en.png" alt="feature-img" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, GoReplay primarily concentrates on replaying captured traffic in its original form, lacking inherent functionality to alter the data during the replay phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify regressions and changes
&lt;/h3&gt;

&lt;p&gt;With Gor, there's a necessity to author middlewares, for tasks such as comparing original and replayed data, monitoring latency fluctuations, and producing analytics from this data. This can add layers of complexity to the testing process.&lt;/p&gt;

&lt;p&gt;In contrast, AREX not only provides traffic recording and playback features but also includes built-in comparison functionalities. These integrated features in AREX simplify the process of contrasting original and replayed data, monitoring performance changes, and extracting insightful analytics, streamlining the overall testing workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  AREX or Goreplay?
&lt;/h3&gt;

&lt;p&gt;In conclusion, when deciding between AREX and GoReplay for traffic replay, the choice hinges on specific testing requirements and preferences. GoReplay offers simplicity and a straightforward approach, making it suitable for scenarios where basic traffic replay is needed without additional complexities. AREX, on the other hand, shines with its advanced features like data sanitization, traffic modification, and built-in comparison capabilities, catering to more intricate testing scenarios. Ultimately, the decision should align with your project’s needs, balancing simplicity against a comprehensive set of features for thorough and effective testing.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
View on &lt;a href="https://github.com/arextest"&gt;GitHub&lt;/a&gt;&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to mock a method call with Java Agent?</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Wed, 29 Nov 2023 06:00:59 +0000</pubDate>
      <link>https://dev.to/arex_test/the-role-of-java-agent-in-mock-testing-52ah</link>
      <guid>https://dev.to/arex_test/the-role-of-java-agent-in-mock-testing-52ah</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Code example&lt;/li&gt;
&lt;li&gt;The implementation in AREX&lt;/li&gt;
&lt;li&gt;Record and replay in-memory data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AREX is an open-source automated regression testing platform based on real requests and data. It enables the accomplishment of bytecode injection with Java Agent to record real traffic in the production environment, and then replays the requests and injects mock data in the testing environment. This enables automatic recording, replaying, and comparison, providing convenience for interface regression testing.&lt;/p&gt;

&lt;p&gt;In general, the basic principles of implementing mocking using Java Agent can be summarized as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the application with an Java Agent loaded.&lt;/li&gt;
&lt;li&gt;In the premain method of the Agent, use a Java bytecode manipulation library like ByteBuddy to modify the bytecode of the target class.&lt;/li&gt;
&lt;li&gt;Locate the method or field in the target class where mocking needs to be applied within the bytecode.&lt;/li&gt;
&lt;li&gt;Use the bytecode manipulation library to modify the implementation of the target method or field to incorporate the mocking logic. This can involve replacing the original method with a mock method or modifying the value of a field.&lt;/li&gt;
&lt;li&gt;Redefine the modified bytecode as a new class and load it into the JVM.&lt;/li&gt;
&lt;li&gt;During runtime, the modified class will be used, enabling the mocking functionality.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AREX Java Agent supports mocking for not only various technology frameworks, but also local time, cached data, and various memory data types. This allows for precise reproduction of the data environment during production execution during replay, without generating dirty data to your database.&lt;/p&gt;

&lt;p&gt;This blog will delve into how AREX achieves automatic mocking of data during traffic replay from a code perspective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code example
&lt;/h3&gt;

&lt;p&gt;Let's take a simple function as an example to understand the implementation. It's a function that converts a given IP string to an integer value. The code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;parseIp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Check if the IP string is legal&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's explain from two perspectives, how to implement traffic playback with the function mock:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record (collect traffic)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When this function is called, we collect the request parameters and corresponding results for later use in traffic replay. The code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Data collection, save the parameters and results in DB&lt;/span&gt;
    &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When traffic replay, the previously collected data is used to implement mocking of this function. The code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By examining the complete code, we can better understand its logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;parseIp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// in the case of replay, the collected data is used as the return result, i.e., Mock&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// in the case of recording, save the parameters and results in DB&lt;/span&gt;
        &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pareseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The implementation in AREX
&lt;/h3&gt;

&lt;p&gt;The implementation in AREX is similar but more complex. The developers don't need to add recording and replaying code in the business logic. The &lt;code&gt;arex-agent&lt;/code&gt; automatically adds the necessary code in the required code. Let's take the Query of MyBatis3 as an example.&lt;/p&gt;

&lt;p&gt;By reading the MyBatis source code, we should know that all Query operations are consolidated in the &lt;code&gt;query&lt;/code&gt; method of the &lt;code&gt;org.apache.ibatis.executor.BaseExecutor&lt;/code&gt; class (excluding Batch operations). The method signature is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MappedStatement&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RowBounds&lt;/span&gt; &lt;span class="n"&gt;rowBounds&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ResultHandler&lt;/span&gt; &lt;span class="n"&gt;resultHandler&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CacheKey&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BoundSql&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;SQLException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query method includes the SQL query and accompanying parameters. The function's result includes the data read from the database. It is obviously appropriate to collect data here, and during replay, the collected data can be used as the result to avoid actual operations performed on database. &lt;/p&gt;

&lt;p&gt;Let's take a look at the code in AREX. For the sake of simplicity, some simplifications have been made, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExecutorInstrumentation&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TypeInstrumentation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nc"&gt;ElementMatcher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TypeDescription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;typeMatcher&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Full name of the class which need code injection&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.apache.ibatis.executor.BaseExecutor"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MethodInstrumentation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;methodAdvices&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// The name of the method that requires code injection. Because the query method has multiple overloads, parameter verification is included.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Collections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;singletonList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MethodInstrumentation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isPublic&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.apache.ibatis.mapping.MappedStatement"&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.apache.ibatis.mapping.BoundSql"&lt;/span&gt;&lt;span class="o"&gt;))),&lt;/span&gt;
                        &lt;span class="nc"&gt;QueryAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// injected code&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QueryAdvice&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnMethodEnter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skipOn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnNonDefaultValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suppress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;onMethodEnter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MappedStatement&lt;/span&gt; &lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                            &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                            &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;BoundSql&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                            &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mockResult"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;RepeatedCollectManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// prevent repeated collection of data caused by nested calls&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InternalExecutor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnMethodExit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onThrowable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suppress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onMethodExit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MappedStatement&lt;/span&gt; &lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;BoundSql&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Thrown&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Return&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mockResult"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;RepeatedCollectManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exitAndValidate&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrowable&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrowable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;            

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;InternalExecutor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;var2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;QueryAdvice&lt;/code&gt; is the code that needs to be injected in the &lt;code&gt;query&lt;/code&gt; method. The code injected through &lt;code&gt;onMethodEnter&lt;/code&gt; will be executed at the beginning of the method, while the code injected by &lt;code&gt;onMethodExit&lt;/code&gt; will be executed before the function returns the result.&lt;/p&gt;

&lt;p&gt;It may be difficult to understand, so let's dump the code of BaseExecutor's query method after code injection and analyze it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MappedStatement&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RowBounds&lt;/span&gt; &lt;span class="n"&gt;rowBounds&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ResultHandler&lt;/span&gt; &lt;span class="n"&gt;resultHandler&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CacheKey&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BoundSql&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;SQLException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;skipOk&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;RepeatedCollectManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InternalExecutor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;skipOk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;var28&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;var28&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;skipOk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skipOk&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// in the case of replay, the original query method body is no longer executed&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// The source code of the query method in BaseExecutor(omitted here), and the only adjustment is to modify the code that is returned in the original method to assign the result to a variable named result&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;var27&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;var27&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrowable&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThrowable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RepeatedCollectManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exitAndValidate&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;InternalExecutor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boundSql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Throwable&lt;/span&gt; &lt;span class="n"&gt;var26&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;var26&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see, the code of &lt;code&gt;onMethodEnter&lt;/code&gt; and &lt;code&gt;onExit&lt;/code&gt; is inserted at the beginning and end respectively. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When send a request to a service, AREX will decide whether to record this request based on the configured recording frequency. If recording is required, all 3rd dependencies accessed during this request will be recorded.&lt;/p&gt;

&lt;p&gt;AREX will record this request by invoking the &lt;code&gt;InternalExecutor.record(ms, parameter, boundSql, result, throwable, "query")&lt;/code&gt; method to store the returned result, core parameters, and other information into AREX's database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replay&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When resend the recorded request, the function will not be executed actually. Instead, AREX will directly return the recorded result previously by invoking &lt;code&gt;InternalExecutor.replay(ms, parameter, boundSql, "query")&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Record and replay in-memory data
&lt;/h3&gt;

&lt;p&gt;The previous example is idempotent function. For idempotent functions, since the returned result remains the same regardless of external factors, there is no need for collecting data and mocking during the recording and replay process.&lt;/p&gt;

&lt;p&gt;For non-idempotent functions, the results returned by the request are different each time due to the influence of the external environment, such as local caches where different environment data may lead to different output results. In such cases, AREX can also record and mock the non-idempotent functions by configure it as dynamic classes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjav4jclnawo0vj5lldt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjav4jclnawo0vj5lldt.png" alt="Image description" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, you can configure the class name, method name (optional, if not configured, it will be applied to all public methods with parameters and return values), and parameter types (optional) Once configured, the &lt;code&gt;arex-agent&lt;/code&gt; will automatically inject similar code into the corresponding methods, enabling data collection and mocking during the replay process.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>java</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How to mock Apollot Configration with AREX Java Agent?</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 16 Nov 2023 05:43:46 +0000</pubDate>
      <link>https://dev.to/arex_test/how-to-mock-apollot-configration-with-arex-java-agent-g87</link>
      <guid>https://dev.to/arex_test/how-to-mock-apollot-configration-with-arex-java-agent-g87</guid>
      <description>&lt;ul&gt;
&lt;li&gt;
What is Apollo

&lt;ul&gt;
&lt;li&gt;Principle of Apollo client implementation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Development Process&lt;/li&gt;
&lt;li&gt;Recording Implementation&lt;/li&gt;
&lt;li&gt;Replay Implementation&lt;/li&gt;
&lt;li&gt;
Version Differences

&lt;ul&gt;
&lt;li&gt;Integration Testing&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current tools that support traffic replication for automated regression testing, such as tcpcopy and diffy, cannot be used for testing non-idempotent interfaces. This is because they record and replay traffic at the network layer outside of the application, which can only validate read-only pages. When it comes to testing interfaces that require writing to the database, it can result in dirty data and even affect the correctness of the business logic. &lt;/p&gt;

&lt;p&gt;Unlike these tools, AREX implements traffic recording and playback internally within the application using AOP (Aspect-Oriented Programming). It utilizes Java Agent and bytecode enhancement technology to record the real traffic in the production environment. This approach avoids the impact of traffic replay on business data, making traffic replication regression testing suitable for various Java-based front-end and back-end business systems.&lt;/p&gt;

&lt;p&gt;The basic principle of mocking in AREX is that the Java Agent intercepts the class loading process and substitutes the original class with a mock implementation.&lt;/p&gt;

&lt;p&gt;Now most open-source components can be mocked with AREX Java Agent. This article will introduce how to mock Apollo Configuration with AREX Agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Apollo
&lt;/h2&gt;

&lt;p&gt;Apollo is a reliable configuration management system. It can centrally manage the configurations of different applications and different clusters. It is suitable for microservice configuration management scenarios. And Configuration changes take effect in real-time.&lt;/p&gt;

&lt;p&gt;Here's the official description of the Apollo base model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Users modify and publish the configuration in the configuration center&lt;/li&gt;
&lt;li&gt;The configuration center notifies Apollo clients of configuration updates&lt;/li&gt;
&lt;li&gt;Apollo client pulls the latest configuration from the configuration center, updates the local configuration, and notifies the application&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Principle of Apollo client implementation
&lt;/h3&gt;

&lt;p&gt;The above diagram briefly describes the principle of Apollo client implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The client and the server maintain a long connection to get the first push of configuration updates. (achieved through Http Long Polling) &lt;/li&gt;
&lt;li&gt;The client also regularly pulls the latest application configuration from the Apollo Configuration Center server.&lt;/li&gt;
&lt;li&gt;After the client gets the latest configuration of the application from the Apollo Configuration Center server, it will be saved in memory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o1wr8xdc4inwljqwdcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0o1wr8xdc4inwljqwdcy.png" alt="Apollo Configuration" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Development Process
&lt;/h1&gt;

&lt;p&gt;From the above figure, we can see that AREX only needs to support Apollo client recording and playback, i.e. Java application projects internally refer to the apollo-client component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.ctrip.framework.apollo&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;apollo-client&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;{apollo-client.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically, there are three ways in which Apollo is commonly used in projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spring Autowired annotation &lt;code&gt;configBean&lt;/code&gt; (internally using EnableApolloConfig annotation)&lt;/li&gt;
&lt;li&gt;Based on Apollo's built-in annotation ApolloConfig, such as the &lt;code&gt;config&lt;/code&gt; object in the code&lt;/li&gt;
&lt;li&gt;API Mode, such as the config1 object in the code below
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="nc"&gt;ConfigBean&lt;/span&gt; &lt;span class="n"&gt;configBean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The first way, internally using EnableApolloConfig annotation&lt;/span&gt;

&lt;span class="nd"&gt;@ApolloConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TEST1.lucas"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The second way&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="n"&gt;config1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The third way, calling getAppConfig&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;config1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAppConfig&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timeout="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timeout"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"switch="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBooleanProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"switch"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"white.list="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;config1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"flight.change.white.list"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"configBean="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;configBean&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Listening for Apollo Configuration Changes&lt;/span&gt;
    &lt;span class="nc"&gt;ConfigChangeListener&lt;/span&gt; &lt;span class="n"&gt;changeListener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;changeEvent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changes for namespace:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;changeEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNamespace&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addChangeListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeListener&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableApolloConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TEST1.sofia"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConfigBean&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${age:0}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${name:}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ApolloJsonValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${resume:[]}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;JsonBean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;jsonBean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If AREX needs to implement the recording and playback of Apollo, it needs to be compatible with these three ways of use. By examining the Apollo source code, it is found that the first two modes, based on the annotations EnableApolloConfig and ApolloConfig, and the last mode, which calls the API, all ultimately create instances using ConfigService.getAppConfig(). This means that the underlying API is shared. Therefore, we can modify these underlying Apollo methods by inserting AREX bytecode, in order to achieve the goal of recording and playback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Implementation
&lt;/h2&gt;

&lt;p&gt;All configuration items in Apollo are distinguished based on namespaces. In order to perform recording, we need to obtain all configuration instances, which means getting the config instance corresponding to each namespace. Further examination of the apollo-client source code reveals that the config instances are maintained in the &lt;code&gt;Map&amp;lt;String, Config&amp;gt; m_configs&lt;/code&gt; of the &lt;code&gt;DefaultConfigManager&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;However, several issues need to be considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;m_configs&lt;/code&gt; property is private and there is no related API to access it.&lt;/li&gt;
&lt;li&gt;This instance is rarely called during business runtime, so it may not be possible to obtain &lt;code&gt;m_configs&lt;/code&gt; through conventional Arex mocking methods.&lt;/li&gt;
&lt;li&gt;After obtaining the &lt;code&gt;m_configs&lt;/code&gt; instance, it is also necessary to obtain the &lt;code&gt;m_configProperties&lt;/code&gt; in the Config class, as this is where the actual configuration data resides.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dependency relationships in UML:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folliruxubt7lo4jnshl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folliruxubt7lo4jnshl2.png" alt="UML" width="455" height="591"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, it is advisable to use reflection to obtain all configurations and record them. This approach would only invoke reflection for recording during the initial startup or when configuration changes occur, which happens infrequently.&lt;/p&gt;

&lt;p&gt;Another consideration is the timing of recording. Take the above code for example, the third way to use Apollo is to create the "config1" instance within the business interface "test()":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="n"&gt;config1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAppConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be regarded as an incremental configuration instance (compared to the first two ways where the full configuration instance is created at project startup using annotations). Therefore, we need to make sure that both can be recorded. The current approach is to perform recording at the "postHandle" point after the main entry servlet/Dubbo interface has completed the request and returned the result. This way, regardless of which way the "config" instance is created, we can obtain and record it.&lt;/p&gt;

&lt;p&gt;If the Apollo configuration changes during recording, we can add modification code to the Apollo source code: &lt;code&gt;com.ctrip.framework.apollo.internals.DefaultConfig#updateAndCalcConfigChanges&lt;/code&gt; method  to listen for change events and reopen our recording switch. Turn on our recording switch so that &lt;strong&gt;we can record to the new configuration the next time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When recording, it will generate a version number to distinguish the batch of test cases recorded in different periods, i.e., the version number acts as a batch concept, as shown in the timeline below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprjd2tifjgmwrn6l1eqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprjd2tifjgmwrn6l1eqf.png" alt="timeline" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Replay Implementation
&lt;/h2&gt;

&lt;p&gt;Similar to the recording implementation, in the case of replay, by using reflection, the &lt;code&gt;m_configProperties&lt;/code&gt; can be assigned values, and the mocked configuration can be used to override the real configuration.&lt;/p&gt;

&lt;p&gt;There are several issues to consider:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to trigger a configuration change listener method set by the application, such as the changeListener method in the Apollo usage above;&lt;/li&gt;
&lt;li&gt; During replay, the long polling of Apollo for configuration changes may overwrite our configuration for replay, which needs to be avoided;&lt;/li&gt;
&lt;li&gt;How to ensure the correctness of configuration data when replaying multiple versions of configurations? &lt;/li&gt;
&lt;li&gt;How to restore the original configurations after replay?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Considering the above issues, the implementation of recording is not comprehensive for replaying and cannot meet these specific scenarios.&lt;/p&gt;

&lt;p&gt;After a deep dive into the source code, we choose to modify the &lt;code&gt;loadApolloConfig&lt;/code&gt; method in the &lt;code&gt;com.ctrip.framework.apollo.internals.RemoteConfigRepository&lt;/code&gt; class. Before requesting the server configuration, we directly return our mocked configuration data. This allows us to &lt;strong&gt;leverage the existing mechanisms in Apollo to trigger the complete configuration update process.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return the mocked configuration directly if the real Apollo-Server service is not called during playback.&lt;/li&gt;
&lt;li&gt;After replay, the method is no longer mocked (the replay is considered complete if there is no replay activity for more than 1 minute), and then the normal logic is executed, i.e. the real configuration is used.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the long polling of Apollo is always running, if the replay activity is finished and Apollo detects that the server configuration is inconsistent with the configuration replayed by AREX, it will trigger the operation to update the local configuration, achieving the goal of restoring the original configuration.&lt;/p&gt;

&lt;p&gt;Regarding the third point mentioned above, how to ensure the correctness of configuration data when replaying multiple versions of configurations?&lt;/p&gt;

&lt;p&gt;The recording operation only occurs during the project startup and when there are changes in the configuration. The generated version number (UUID) is also used as the replay version number. If recorded multiple version numbers, the replay is done sequentially according to different numbers. This means that the version numbers generated by AREX are used to differentiate different versions of configurations. The implementation approach is to set the "releaseKey" attribute of the constructed Apollo configuration entity, &lt;code&gt;com.ctrip.framework.apollo.core.dto.ApolloConfig&lt;/code&gt; , to our AREX version number every time the configuration is replayed. This ensures the correctness of playback for multiple versions of configuration data.&lt;/p&gt;

&lt;p&gt;The "releaseKey" is a crucial field for communication between Apollo client and server. The server uses this field to determine if the configuration is consistent with the client. If not, return a new "releaseKey" value; otherwise, return a 304 status.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version Differences
&lt;/h2&gt;

&lt;p&gt;It is also necessary to be aware of the differences in source code between different versions of the decorated apollo-client.&lt;/p&gt;

&lt;p&gt;Some methods or classes may have variations in different versions. Before deciding to modify underlying methods, it is better to examine the differences between different versions first. Otherwise, there is a possibility of failure due to inconsistencies between the Apollo client version of the user's project and the version modified by AREX Java Agent.&lt;/p&gt;

&lt;p&gt;For example, the method we modify, &lt;code&gt;com.ctrip.framework.apollo.internals.DefaultConfig#updateAndCalcConfigChanges&lt;/code&gt; has differences in the input parameters between versions 1.0.0 and 1.2.0.&lt;/p&gt;

&lt;p&gt;v1.0.0&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnnpiawr3t8aktxfx4lz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnnpiawr3t8aktxfx4lz.png" alt="v1.0.0" width="800" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;v1.2.0&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo199i0j8w78bwe7tra7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo199i0j8w78bwe7tra7u.png" alt="v1.2.0" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When modifying this method, we need to ensure compatibility with such differences so that our injected bytecode can work properly across different versions. &lt;/p&gt;

&lt;p&gt;It is recommended to choose a lower version when modifying with components in order to maintain as much compatibility with existing code as possible and to follow the open–closed principle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration Testing
&lt;/h2&gt;

&lt;p&gt;Now we need to perform integration testing between Agent and Schedule Service.&lt;/p&gt;

&lt;p&gt;When a user starts to replay, the Schedule Service first groups all the configuration version numbers generated during recording. After grouping all the test cases within that time, the service switches the version number before each replay, informing the AREX agent that Apollo's configuration needs to be played back.&lt;/p&gt;

&lt;p&gt;This response for version switch request is not considered as the replay result, but simply as a version pre-heating. The real replay process will be initiated once the configuration corresponding to the version number is successfully switched. The same process applies when switching to other version numbers, as shown in the following diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjyww3326idsfhk2vdyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjyww3326idsfhk2vdyf.png" alt="Integration Testing" width="800" height="939"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same batch of cases has the same version number, which only recorded or replayed once.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Reproduce production bugs locally with AREX</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Tue, 07 Nov 2023 07:41:13 +0000</pubDate>
      <link>https://dev.to/arex_test/reproduce-production-bugs-locally-with-arex-2daa</link>
      <guid>https://dev.to/arex_test/reproduce-production-bugs-locally-with-arex-2daa</guid>
      <description>&lt;ul&gt;
&lt;li&gt;How to reproduce issues with AREX&lt;/li&gt;
&lt;li&gt;
Deploy and start AREX

&lt;ul&gt;
&lt;li&gt;Step 1: Deploy AREX with Docker&lt;/li&gt;
&lt;li&gt;Step 2: Start the application with AREX Java Agent&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Reproduce online issues locally 

&lt;ul&gt;
&lt;li&gt;Step 3: Record requests for bugs in the production environment&lt;/li&gt;
&lt;li&gt;Step 4: Debugging with local replay&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the software development cycle, software bugs are a natural occurrence. Reproducing and fixing issues in the development environment is a common activity for developers.&lt;/p&gt;

&lt;p&gt;Reproducing production issues locally allows us to test and validate any proposed fixes before deploying them. This is essential for preventing regressions and ensuring the fix resolves the issue. It can also help identify and resolve other problems that may have been overlooked.&lt;/p&gt;

&lt;p&gt;However, what often troubles developers is the differences in configuration and data between the production and local environments (such as data in the database, data in the cache, etc.), issues that occur in the production environment cannot be quickly reproduced in the local testing environment, making troubleshooting much more challenging.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reproduce issues with AREX
&lt;/h2&gt;

&lt;p&gt;AREX is an automated regression testing tool, that can record traffic and data in the production environment by Java agent and replay them in the testing environment to identify the correlation between code changes and result differences.&lt;/p&gt;

&lt;p&gt;AREX Java Agent can mock all external dependencies of the recorded real request during recording. This ensures that the data and behavior in the development environment are identical to the production environment, enabling issue reproduction and troubleshooting locally.&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll show you how to use AREX to reproduce issues. Let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy and start AREX
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Deploy AREX with Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/arextest/deployments.git 
cd deployments docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Start the application with AREX Java Agent
&lt;/h3&gt;

&lt;p&gt;AREX Agent is the core component for traffic recording. Therefore, before using the recording feature, it is necessary to configure the AREX Agent for the application under test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:arextest/arex-agent-java.git 
mvn clean install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After compilation, you will find a new folder named "arex-agent-jar" in the "arex-agent-java" directory, which contains two JAR files.&lt;/p&gt;

&lt;p&gt;To configure the Java Agent for the application to be tested, add the Java Agent configuration to the startup of the Java application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug9setzn983ykrt4nz8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fug9setzn983ykrt4nz8q.png" alt="Image description" width="751" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The complete environment variables are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JAVA_TOOL_OPTIONS='-javaagent:E:/github-arex/arex-agent-java/arex-agent-jar/arex-agent-0.1.0.jar'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Program arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-Darex.service.name=arex-community-test6 -Darex.storage.service.host=10.5.153.1:8093 -Darex.enable.debug=true  -Dspring.datasource.url=jdbc:mysql://10.5.153.1:13306/community?useUnicode=true&amp;amp;characterEncoding=utf-8 -Dspring.datasource.username=arex_admin -Dspring.datasource.password=arex_admin_password -Dspring.redis.host=10.5.153.1 -Dspring.redis.port=16379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;View the log at startup:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6pv9zzae32hg2sfbt53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6pv9zzae32hg2sfbt53.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will see the message "ArexJavaAgent installed" in the printout, indicating that the Agent has been successfully installed.&lt;/p&gt;

&lt;p&gt;At the same time, you can see Java printing out command-line parameter information, such as Agent address and Storage service address, as indicated by the arrows in the above image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce online issues locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 3: Record requests for bugs in the production environment
&lt;/h3&gt;

&lt;p&gt;A normal HTTP request and the response are as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya5y85ru6psuijzrcqlb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fya5y85ru6psuijzrcqlb.png" alt="Image description" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a bug occurs in a production request, configure all the settings for the request in AREX, and click on "action.record" to enable recording this request, as shown in the figure below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hnhqn11fg8mjugpqyw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hnhqn11fg8mjugpqyw2.png" alt="Image description" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After clicking the button, a new key named "arex-force-record" with a value of "true" will be added to the Header List. Then save the changes.&lt;/p&gt;

&lt;p&gt;Send the request and the response headers will generate a new key called "arex-record-id" with a value of "AREX-172-20-0-4-708656889122" (a unique value randomly generated by the AREX Agent). This indicates that the request has been successfully recorded and all the data that the request depends on in the production environment has been stored in AREX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprtbqbre7z9kgr8youd7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprtbqbre7z9kgr8youd7.png" alt="Image description" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Debugging with local replay
&lt;/h3&gt;

&lt;p&gt;Create a new request and modify the environment of the URL to the local environment. In the AREX request headers, add a key "arex-record-id" with the value set to the previously generated "AREX-172-20-0-4-708656889122". After clicking save, send the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhdrp6txirr2ws2rb24e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhdrp6txirr2ws2rb24e.png" alt="Image description" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can debug the issue step by step in your IDEA environment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fch9bri0k7q1b6znvn8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fch9bri0k7q1b6znvn8.png" alt="Image description" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After debugging is completed, you can view the response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4y5oi37eg6ulv3htx24u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4y5oi37eg6ulv3htx24u.png" alt="Image description" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response message contains an "arex-replay-id" key with a value of "AREX-172-20-0-4-708658384473" (an ID randomly generated by the AREX Agent for analysis purposes). It means that the request has been successfully replayed.&lt;/p&gt;

&lt;p&gt;The above is the complete process of using the AREX traffic recording feature to quickly reproduce and debug online issues locally.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>testing</category>
      <category>showdev</category>
    </item>
    <item>
      <title>A Real Automated Regression Testing Platform with Recording and Replay Testing (Open-Source)</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Fri, 27 Oct 2023 09:07:45 +0000</pubDate>
      <link>https://dev.to/arex_test/a-real-automated-regression-testing-platform-with-recording-and-replay-testing-open-source-5f7p</link>
      <guid>https://dev.to/arex_test/a-real-automated-regression-testing-platform-with-recording-and-replay-testing-open-source-5f7p</guid>
      <description>&lt;p&gt;As the company's businesses scale, the system is becoming increasingly complex. The R&amp;amp;D testing team is facing various challenges, including high business complexity, large data construction workload, full regression testing, high communication costs, large maintenance of test data, and automation of case management. Each of these will affect the efficiency and quality of the testing team, posing challenges to the software development process.&lt;/p&gt;

&lt;p&gt;In summary, there are two difficulties: cost and complexity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt;: there will always be a trade-off between cost and quality. It's a quite challenge to ensure quality while iterating quickly and ensuring quality within limited inputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: when business rules accumulate over time, the complexity of business processes, rules, scenarios, and data processing increases exponentially after stacking, posing great challenges to the quality of testing work.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite the availability of many automation testing tools on the market today, there are still two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Automated testing mainly focuses on automating execution. The maintenance work is still done manually, resulting in a less-than-optimal output ratio.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In scenarios where a large amount of test data needs to be constructed, regression testing involves a wide scope, releases are frequent, and data validation and writing are required, both manual and automated testing still face a huge workload in terms of maintaining test cases and data, and the pain points of testing have not been effectively addressed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Replay Traffic Testing
&lt;/h2&gt;

&lt;p&gt;Replay traffic refers to capturing the production traffic and replaying it in a test environment, allowing us to exercise updated systems to simulate actual production conditions.&lt;/p&gt;

&lt;p&gt;We used this concept to build an automated testing platform AREX, which combines recording, playback, and comparison. (&lt;a href="https://github.com/arextest"&gt;https://github.com/arextest&lt;/a&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Record: record the production requests as well as the data involved in the request processing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replay: replay the requests and mock the data involved in the invocation chain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comparative Analysis and Reporting: compare and analyze the responses from recording and replay, and get a comprehensive report for the analysis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is AREX?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uywfso5ekvya8kevox5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uywfso5ekvya8kevox5.png" alt="arex" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AREX is an open-source automation test platform. It records real traffic in the production environment and mocks all invocations to third-party dependencies with Java Agent bytecode injection technology, then replays the request and mock data in the test environment. Furthermore, it compares the recorded and replayed response messages, and lists the differences, allowing developers and testers to quickly troubleshoot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The process of replay traffic testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Record
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g7a81uj599wwgvrxf8g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1g7a81uj599wwgvrxf8g.png" alt="Record" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AOP (Aspect Oriented Programming) is &lt;strong&gt;a technique for building common, reusable routines that can be applied applicationwide&lt;/strong&gt;. A Java Agent can be used to apply AOP principles by intercepting method calls and weaving in additional behavior. It provides the necessary infrastructure to implement AOP in Java applications.&lt;/p&gt;

&lt;p&gt;In the flow of a request, the AREX Java Agent records the request information of Java applications in the production environment. It sends this information to the &lt;strong&gt;AREX Storage Service&lt;/strong&gt;, which imports and stores the data in a MongoDB database. &lt;/p&gt;

&lt;h3&gt;
  
  
  Replay
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf642xnigl24q4aacobp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf642xnigl24q4aacobp.png" alt="replay" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During replay, the AREX Schedule Service retrieves the recorded data (requests) of the tested application from the database according to the user's specifications. It then sends interface requests to the target verification service. At the same time, the Java Agent returns the recorded responses of external dependencies (external requests/DB) to the tested application. After the target service completes the request logic, it returns the response message. &lt;/p&gt;

&lt;h3&gt;
  
  
  Comparative Analysis and Reporting
&lt;/h3&gt;

&lt;p&gt;After replay testing, AREX compares the recorded response with the playback response to verify the correctness of the system logic. It then utilizes the comparison results to generate a testing report to review. Throughout this process, the AREX Cache Service (Redis) is responsible for caching mock data and comparison results during the replay process, improving the efficiency of the comparisons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technology Stack of AREX Java Agent
&lt;/h3&gt;

&lt;p&gt;Due to the good performance and code readability, we chose the Byte Buddy library to modify specific bytecode instructions from Java code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10jd1zn754qcllckvewh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10jd1zn754qcllckvewh.png" alt="Byte Buddy" width="800" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, we use SPI (Service Provider Interface), which is an API proposed to be implemented by a third-party provider. It can be used as an extension or replaceable by existing implementations. Our injection component is implemented through this plug-in model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg4obcuyhayr520wxr87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg4obcuyhayr520wxr87.png" alt="SPI" width="504" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ad8i9te1pai95x27tv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3ad8i9te1pai95x27tv.png" alt="Tracing" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While recording, AREX will capture the request and response messages of the main servlet and third-party dependencies as a complete test case. It's a challenge to trace these data. &lt;/p&gt;

&lt;p&gt;To address this problem, the AREX Java agent generates a unique Record ID and saves it to the Thread Local variable. Injecting the agent code at the beginning and end of the application function, this code reads the value in the Thread Local variable and stores it together with the intercepted data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Record and Replay Solution
&lt;/h3&gt;

&lt;p&gt;Let's start with a simple function as an example to understand how to record and replay. Suppose we have the following function that converts a given IP string to an integer value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Check whether IP address is legal&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Record (traffic collection)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When this function is called, we save the corresponding request parameters and the return result for later use in traffic replay. Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Data collection, saving parameters and results in DB&lt;/span&gt;
    &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Replay (traffic replay)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While replaying the traffic, we can use the previously collected data to automatically mock this function, without actually sending the request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By examining the complete code, we can better understand the logic :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;parseIp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// replay scenes, use the collected data as the return result, which is essentially mock.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"parseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipArray&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;]);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Recorded scenes, save the parameters and results to the database&lt;/span&gt;
        &lt;span class="nc"&gt;DataService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pareseIp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compatible Component Versions
&lt;/h3&gt;

&lt;p&gt;Popular components often have multiple versions that are used on different systems at the same time, and the implementations of the different versions may be very different or even incompatible.&lt;/p&gt;

&lt;p&gt;To address this problem, AREX makes multiple versions compatible. In the application startup, the AREX Agent will capture all the dependent package information, such as the JAR package Manifest.MF file, from the Manifest to get the version information of the library, and then according to the version information to start the corresponding AREX injection code, thus realizing the realization of multiple versions of compatibility.&lt;/p&gt;

&lt;p&gt;As shown in the following figure, the version range of the current injection script adaptation is set so that AREX can identify the versions of the components that the application depends on before these classes are loaded, and then later match the versions when the classes are loaded to ensure correct code injection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkal1whxeanebe9wtg8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdkal1whxeanebe9wtg8e.png" alt="version" width="800" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Code Isolation
&lt;/h4&gt;

&lt;p&gt;Since most scenarios are recorded in the production environment and replayed in the test environment, stability is of paramount importance. For the stability of the system and to prevent the Agent's code from affecting the code execution of the application under test, AREX has realized code isolation and interoperability.&lt;/p&gt;

&lt;p&gt;The AREX core JAR is loaded in an independent ClassLoader, which is not interoperable with the user's application code. To ensure that the injected code can be accessed correctly at runtime, a simple modification to the ClassLoader is made.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mock Time
&lt;/h4&gt;

&lt;p&gt;Many business scenarios are time-sensitive. Imagine that during the replay of requests, the recorded time has expired, resulting in failed testing.&lt;/p&gt;

&lt;p&gt;Here we implement &lt;code&gt;currentTimeMillis()&lt;/code&gt; to proxy the original call of Java's &lt;code&gt;currentTimeMillis()&lt;/code&gt;. While recording the traffic, capture the current time for each test case. By using a proxy for System.currentTimeMillis(), the time during replay will be replaced with simulated time that matches the time of recording, to mock time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zSVe-9in--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://arextest.com/assets/images/17-88069676757027ac76b0b6606c3a273a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zSVe-9in--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://arextest.com/assets/images/17-88069676757027ac76b0b6606c3a273a.png" alt="mock" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Mock Caches
&lt;/h4&gt;

&lt;p&gt;Applications in real production environments often utilize different types of caches to enhance runtime performance. However, the variations in cached data can lead to inconsistent execution results.&lt;/p&gt;

&lt;p&gt;These dynamic classes can be mocked in AREX, by accessing the local cache method configured as a dynamic class, equivalent to your customization of the method for mock, which will be recorded in the production environment where you configure the dynamic class method data, replay corresponding to the match out of the data returned.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you do with AREX?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Diversity validation
&lt;/h3&gt;

&lt;p&gt;To verify the correctness of business logic after modifying the system, merely checking the return results is not enough. Usually, it is also necessary to verify the correctness of intermediate process data, such as whether the data content written to the database by the business system is correct.&lt;/p&gt;

&lt;p&gt;In response to this, AREX also supports validating the data writing to the third-party dependencies.&lt;/p&gt;

&lt;p&gt;During the recording and replay process, AREX will record the database requests sent to the outside by both the old and new versions of the system, and compare these two requests. If there are differences, they will be displayed in the report.&lt;/p&gt;

&lt;p&gt;Since AREX mocks all requests to third-party dependencies, it supports the verification of data in databases, message queues, Redis, and even runtime memory data. Moreover, during the playback process, it does not generate calls to the database, so there is no dirty data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reproduce the Production Issues
&lt;/h3&gt;

&lt;p&gt;In actual usage, AREX can also be used to quickly localize production issues.&lt;/p&gt;

&lt;p&gt;After a production issue occurs, due to version differences, data differences, and other issues, it can be difficult for developers to reproduce on their local machines, and the cost of debugging is high and time-consuming.&lt;/p&gt;

&lt;p&gt;By using AREX, you can force the recording of problematic cases (the response message will generate a unique Record ID) on the production environment. Then start your local development environment and add this Record ID to the request message header. This allows you to restore the recorded request and data on your local machine using the playback function, and then directly debug the production issue using local code.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Mock Testing: Write a Java Agent Plugin to Mock Your Custom Components</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Tue, 17 Oct 2023 08:36:18 +0000</pubDate>
      <link>https://dev.to/arex_test/mock-testing-write-a-java-agent-plugin-to-mock-your-custom-components-1a9e</link>
      <guid>https://dev.to/arex_test/mock-testing-write-a-java-agent-plugin-to-mock-your-custom-components-1a9e</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Mock Testing with Java agent&lt;/li&gt;
&lt;li&gt;AREX Agent&lt;/li&gt;
&lt;li&gt;Setting up the development environment&lt;/li&gt;
&lt;li&gt;Step 1: Create &lt;code&gt;DalClientModuleInstrumentation&lt;/code&gt; Class&lt;/li&gt;
&lt;li&gt;Step 2: Bytecode Modification&lt;/li&gt;
&lt;li&gt;Step 3: Implementation of recording and replay&lt;/li&gt;
&lt;li&gt;Deployments&lt;/li&gt;
&lt;li&gt;Debug&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mock Testing with Java agent
&lt;/h2&gt;

&lt;p&gt;Mock testing is a software testing method used to simulate the behavior of certain components or dependencies in a system. The purpose of mock testing is to isolate the system under test and verify the correctness of interactions between the system and its dependencies.&lt;/p&gt;

&lt;p&gt;Java Agent is a Java technology that allows application startup modification and enhancement of bytecode. With Java Agent, it is possible to dynamically modify the behavior of target classes at runtime, including intercepting method calls and modifying return values. This provides a possibility for implementing Mock.&lt;/p&gt;

&lt;p&gt;By implementing a proxy class or using bytecode manipulation libraries like Byte Buddy or ASM, you can modify the behavior of the target classes. This allows you to return mock data or execute custom logic instead of the actual behavior of the dependencies.&lt;/p&gt;

&lt;p&gt;By loading Java Agent into the target application, it can intercept and modify calls to open source components at application startup, thus enabling Mock of open source components. This can isolate the system under test during testing and verify the correctness of interactions between the system and its dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  AREX Agent
&lt;/h2&gt;

&lt;p&gt;AREX provides an out-of-box agent file that could be attached to any applications with Java 8+ and dynamically weaves solid bytecode into your existing code to record the real data of live traffic, and further use and replay it for mocking, testing, and debugging purposes.&lt;/p&gt;

&lt;p&gt;Let's take a simple component &lt;code&gt;DalClient&lt;/code&gt; as an example to illustrate how AREX Agent implements mocking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// return the value when recording&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// no value to return thus call the remote interface when replaying&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up the development environment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download the code and run mvn install.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/arextest/arex-agent-java.git 
mvn clean &lt;span class="nb"&gt;install&lt;/span&gt; // &lt;span class="nb"&gt;install &lt;/span&gt;arex-agent-java dependencies that may be needed into your &lt;span class="nb"&gt;local &lt;/span&gt;maven repository.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a new Java project from IntelliJ IDEA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31jg03t4dwujuio2f0vj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31jg03t4dwujuio2f0vj.png" alt="new project" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, add the AREX Agent dependencies to the pom file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Some basic classes and tools used by arex-agent, such as recording and playback related components, configuration information, etc. --&amp;gt;&lt;/span&gt;         
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.arex&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;arex-instrumentation-api&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${arex.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- serialization related components --&amp;gt;&lt;/span&gt;         
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.arex&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;arex-serializer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;${arex.version}&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Replace it with the component you need to mock in the actual development. --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.your.company.dal&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;dalclient&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Create &lt;code&gt;DalClientModuleInstrumentation&lt;/code&gt; Class
&lt;/h2&gt;

&lt;p&gt;The arex-agent-java loads and instantiates plugins using the SPI (Service Provider Interface) mechanism. Therefore, we declare a class called &lt;code&gt;DalClientModuleInstrumentation&lt;/code&gt; with the &lt;code&gt;com.google.auto.service.AutoService&lt;/code&gt; annotation. This class serves as the entry point for modifying &lt;code&gt;DalClient&lt;/code&gt; and will be recognized by the arex-agent-java (@AutoService).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@AutoService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ModuleInstrumentation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DalClientModuleInstrumentation&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ModuleInstrumentation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DalClientModuleInstrumentation&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Plugin module name, you can specify a different version match if the code difference between different versions of your DalClient component is relatively large and to be supported in separate versions:&lt;/span&gt;
       &lt;span class="c1"&gt;// ModuleDescription.builder().name("dalclient").supportFrom(ComparableVersion.of("1.0")).supportTo(ComparableVersion.of("2.0")).build();&lt;/span&gt;
       &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"plugin-dal"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TypeInstrumentation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;instrumentationTypes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// classes that have undergone bytecode modification to change the behavior of the DalClient component&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;singletonList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DalClientInstrumentation&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when it's necessary to distinguish between version numbers?&lt;/p&gt;

&lt;p&gt;If there're differences between two versions of the &lt;code&gt;DalClient&lt;/code&gt; component, eg., the name of "invoke()" method in version 1.0.0 is changed to "invokeAll()" in version 2.0.0. In this case, the code modified by the Agent plugin cannot cover both versions of the DalClient framework simultaneously. Therefore, adapting the code specifically for different versions may be necessary.&lt;/p&gt;

&lt;p&gt;The implementation details can refer to the adaptation logic for different Dubbo versions in the &lt;code&gt;arex-instrumentation/dubbo/&lt;/code&gt; module of the arex-agent-java project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0e1apcv2peucb83j6ou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0e1apcv2peucb83j6ou.png" alt="DalClientModuleInstrumentation" width="648" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The version matching is implemented based on the contents of the META-INF/MANIFEST.MF file in the jar package of the component.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facrp45uw59nabq6wci9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facrp45uw59nabq6wci9z.png" alt="DalClientModuleInstrumentation" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Bytecode Modification
&lt;/h2&gt;

&lt;p&gt;To modify the bytecode, create a new file named &lt;code&gt;DalClientInstrumentation.java&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Modification of the DalClient source code is quite simple. Just find the underlying implementation methods, preferably using a common API, and then using bytecode manipulation tools like Byte Buddy to modify this API by adding our own code to implement the mocking functionality.&lt;/p&gt;

&lt;p&gt;Here's the DalClient source code to be modified:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.your.company.dal&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DalClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;QUERY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSERT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;QUERY:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"query:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;INSERT:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"insert:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;UPDATE:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"update:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"unknown action:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;QUERY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;INSERT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;UPDATE&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;Action&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In regular business projects, the DalClient is typically invoked using the &lt;code&gt;dalClient.query(key)&lt;/code&gt; method. From the above source code, it can be observed that the underlying implementation is achieved through the &lt;code&gt;invoke&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Therefore, by modifying the &lt;code&gt;invoke&lt;/code&gt; method and injecting recording and playback code, the functionality of the DalClientInstrumentation class can be summarized as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DalClientInstrumentation&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TypeInstrumentation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ElementMatcher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TypeDescription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;typeMatcher&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
        &lt;span class="c1"&gt;// Path to the DalClient class to be modified&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.your.company.dal.DalClient"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MethodInstrumentation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;methodAdvices&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ElementMatcher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MethodDescription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoke"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// the method tobe modified&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.your.company.dal.DalClient$Action"&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt; &lt;span class="c1"&gt;// The first parameter type of this method. This is to distinguish that there may be methods with the same name but different parameter types&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;takesArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java.lang.String"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt; 
            &lt;span class="c1"&gt;// The InvokeAdvice class is the code we need to inject in the invoke method         &lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;singletonList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MethodInstrumentation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InvokeAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt; 
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MethodInstrumentation, ElementMatcher, named, takesArgument, and other methods in the above code are all the ByteBuddy API. AREX Agent edits bytecode with &lt;a href="https://bytebuddy.net/"&gt;ByteBuddy &lt;/a&gt;for recording and replay. (See more details, refer to: &lt;a href="https://bytebuddy.net/#/tutorial"&gt;https://bytebuddy.net/#/tutorial&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Implementation of recording and replay
&lt;/h2&gt;

&lt;p&gt;Here is the code injected into the &lt;code&gt;DalClient#invoke&lt;/code&gt; method, implementing &lt;code&gt;InvokeAdvice&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvokeAdvice&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// OnMethodEnter is the operation that executes before the modified method (invoke) logic is called&lt;/span&gt;
    &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnMethodEnter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skipOn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnNonDefaultValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;suppress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;onEnter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Obtaining a reference to the first parameter (action) of the modified method&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Obtaining a reference to the second parameter (param) of the modified metho&lt;/span&gt;
                                  &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mockResult"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// mockResult is the customized variables within this method &lt;/span&gt;
        &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DalClientAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// replay&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notIgnoreMockResult&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// OnMethodExit is the operation that executes before the end of the modified method (invoke)&lt;/span&gt;
    &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OnMethodExit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suppress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; 
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onExit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                              &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Argument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                              &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mockResult"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                              &lt;span class="nd"&gt;@Advice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Return&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// returned result of the method&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notIgnoreMockResult&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// assigns the replayed result to the result variable&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;DalClientAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// recording logic&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class is designed to inject code before and after the modified &lt;code&gt;invoke&lt;/code&gt; method is invoked, enabling the implementation of recording and replay functionality.&lt;/p&gt;

&lt;p&gt;In which:&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;skipOn = Advice.OnNonDefaultValue&lt;/code&gt; parameter indicates that if &lt;code&gt;mockResult != null &amp;amp;&amp;amp; mockResult.notIgnoreMockResult()&lt;/code&gt; is true (non-default value, where the default value for a boolean type is false), the original logic of the method will be skipped. In other words, the original method logic will not be executed, and instead, the mocked value will be returned. If it is false, the original method logic will be executed as usual.&lt;/p&gt;

&lt;p&gt;The modified bytecode is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DalClientInstrumentation&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TypeInstrumentation&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// replay          &lt;/span&gt;
        &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DalClientAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockResult&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notIgnoreMockResult&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResult&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// the original logic&lt;/span&gt;
        &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;QUERY:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"query:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;INSERT:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"insert:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nl"&gt;UPDATE:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"update:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"unknown action:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;DalClientAdvice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// record&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the Aspect-Oriented Programming (AOP), this class inserts our code before and after the method invocation. In the case of replay, it returns the mocked result without executing the original logic. For recording, it records the result before it returns.&lt;/p&gt;

&lt;p&gt;The code of &lt;code&gt;DalClientAdvice&lt;/code&gt; is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DalClientAdvice&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// record&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needRecord&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Mocker&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buildMocker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTargetResponse&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Serializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serialize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="nc"&gt;MockUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recordMocker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// replay&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt; &lt;span class="nf"&gt;replay&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContextManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;needReplay&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Mocker&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buildMocker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replayBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;MockResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="nc"&gt;DalClientInstrumentation&lt;/span&gt;  &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Mocker&lt;/span&gt; &lt;span class="nf"&gt;buildMocker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DalClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Action&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mocker&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createDatabase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toLowerCase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTargetRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setBody&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above is just a simple demo. To see more details, refer to the &lt;code&gt;arex-instrumentation&lt;/code&gt; module of the &lt;code&gt;arex-agent-java&lt;/code&gt; project for specific usage, which has a lot of implementations that modify various middleware.&lt;/p&gt;

&lt;p&gt;Summarized in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a entry class called &lt;code&gt;DalClientModuleInstrumentation&lt;/code&gt;, which will be loaded when &lt;code&gt;arex-agent-java&lt;/code&gt; starts.&lt;/li&gt;
&lt;li&gt;Create a class called &lt;code&gt;DalClientInstrumentation&lt;/code&gt; to specify which class and method to modify, as well as when to execute the modifications, and inform the AREX Agent accordingly.&lt;/li&gt;
&lt;li&gt;Create a class called &lt;code&gt;DalClientAdvice&lt;/code&gt; to implement the actual recording and replay functionality, or any custom logic you want to implement.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deployments
&lt;/h2&gt;

&lt;p&gt;After completing the development, you can test whether your plugin works correctly by following these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the command &lt;code&gt;mvn clean compile package&lt;/code&gt; to generate the plugin JAR file (it will be located in the &lt;code&gt;/target&lt;/code&gt; directory of your project by default).&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;arex-agent-java&lt;/code&gt; project, run the command &lt;code&gt;mvn clean compile package&lt;/code&gt; to generate the &lt;code&gt;arex-agent-jar&lt;/code&gt; directory (located in the root directory of the project).&lt;/li&gt;
&lt;li&gt;Create a new folder named &lt;code&gt;extensions&lt;/code&gt; in the &lt;code&gt;arex-agent-jar&lt;/code&gt; directory to store the extension JAR files.&lt;/li&gt;
&lt;li&gt;Place the generated plugin JAR file (e.g., &lt;code&gt;plugin-dal-1.0.0.jar&lt;/code&gt;) into the &lt;code&gt;extensions&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AREX Agent loads all JAR files in the &lt;code&gt;extensions&lt;/code&gt; folder as extension features during startup. The directory structure is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F541alc2q3spu0wezh5v7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F541alc2q3spu0wezh5v7.png" alt="directory structure" width="537" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zzbu6hvxuui54ofzdpe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zzbu6hvxuui54ofzdpe.png" alt="directory structure" width="300" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can debug your plugin. Start your business application, making sure to attach the agent in the VM options:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5xe74k42r7s8gefua21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5xe74k42r7s8gefua21.png" alt="debug" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The detailed parameters are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-javaagent:&amp;lt;your-project&amp;gt;\arex-agent-java\arex-agent-0.3.8.jar
-Darex.service.name=&amp;lt;your-service-name&amp;gt;
-Darex.storage.service.host=&amp;lt;arex-storage-service-ip:port&amp;gt;
-Darex.enable.debug=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By starting the project in this way, the agent will be able to load the plugin package.&lt;/p&gt;

&lt;p&gt;If the project references the internal &lt;code&gt;DalClient&lt;/code&gt; component, you will be able to see the name of the plugin declared in &lt;code&gt;DalClientModuleInstrumentation&lt;/code&gt; in the console after starting the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzcylbsdk556j1h9iche.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzcylbsdk556j1h9iche.png" alt="console" width="800" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the console outputs the log message &lt;code&gt;[arex] installed instrumentation module: plugin-dal&lt;/code&gt;, it means that the plugin has been recognized and loaded successfully.&lt;/p&gt;

&lt;p&gt;Next, test if the plugin is functioning correctly. As shown below, if an interface in the project invokes the &lt;code&gt;query&lt;/code&gt; method of the &lt;code&gt;DalClient&lt;/code&gt; component, you can observe whether the plugin can record or replay the &lt;code&gt;invoke&lt;/code&gt; method of the DalClient component. If the recording is successful, the following log message will be printed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpuwtzae95j18r2309tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpuwtzae95j18r2309tb.png" alt="log" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, to test the replay functionality, include the header &lt;code&gt;arex-record-id:AREX-10-32-179-120-2126724921&lt;/code&gt; (the &lt;code&gt;recordId&lt;/code&gt; generated during recording) in the request header. After the successful replay, the console will also output log messages related to &lt;code&gt;[arex] replay category: Database, operation: query&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debug
&lt;/h2&gt;

&lt;p&gt;If the plugin is not working properly or not behaving as expected, you can troubleshoot from the following two points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the bytecode in the &lt;code&gt;arex-agent-jar/bytecode-dump&lt;/code&gt; folder。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;bytecode-dump&lt;/code&gt; folder holds the modified bytecode files where you can find the class files before and after modification here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ac3weskw1g0pn1c8zwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ac3weskw1g0pn1c8zwp.png" alt="bytecode-dump" width="760" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Drag and drop the above two files into your IDE to open them directly. Confirm whether the modifications were successful. You can compare the code logic before (left side) and after (right side) the plugin modifications, as shown in the comparison image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rwkas0sc8t0yxgv1ifl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3rwkas0sc8t0yxgv1ifl.png" alt="IDE" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the modifications were not successful, please refer to the source code of the project demo at &lt;a href="https://github.com/arextest/arex-agent-java-extension-demo"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the code modifications were successful but the behavior is not as expected, you can debug the plugin source code in your IDE by following these steps:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Import the &lt;code&gt;arex-agent-java&lt;/code&gt; project into the IDE of your project by following these steps: File → New → Module from Existing Sources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y8kuvc4tbbdhx0n6oz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1y8kuvc4tbbdhx0n6oz5.png" alt="project" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the local &lt;code&gt;arex-agent-java&lt;/code&gt; project, and then choose Maven to import it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc42mipsxtd44ak3whexl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc42mipsxtd44ak3whexl.png" alt="project" width="584" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Import the plugin project into the IDE of your project. Then you will have the source code for the business project, as well as the &lt;code&gt;arex-agent-java&lt;/code&gt; and &lt;code&gt;plugin-dal&lt;/code&gt; projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtq6fdi4k2jcomfyl3lh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtq6fdi4k2jcomfyl3lh.png" alt="plugin project" width="530" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, you can set breakpoints in both of these projects to debug the code of the Agent and the plugin, as shown in the following image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6waai7vxup87nzkcuul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6waai7vxup87nzkcuul.png" alt="debug" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>AREX: How to Debug API in the browser</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 12 Oct 2023 07:00:59 +0000</pubDate>
      <link>https://dev.to/arex_test/arex-how-to-debug-api-in-the-browser-26p9</link>
      <guid>https://dev.to/arex_test/arex-how-to-debug-api-in-the-browser-26p9</guid>
      <description>&lt;ul&gt;
&lt;li&gt;What is AREX?&lt;/li&gt;
&lt;li&gt;
Challenge 1: Cross-Origin Restrictions

&lt;ul&gt;
&lt;li&gt;Solutions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Challenge 2: API Debugging

&lt;ul&gt;
&lt;li&gt;Solutions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Challenge 3: Binary serialization

&lt;ul&gt;
&lt;li&gt;Solution&lt;/li&gt;
&lt;li&gt;
Code Analysis

&lt;ul&gt;
&lt;li&gt;File to Base64&lt;/li&gt;
&lt;li&gt;Base64 to File&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Debugging APIs is vital in software development. It is a critical step to validate and test the effectiveness and correctness of application interfaces. However, traditional API debugging methods typically rely on separate tools or desktop applications, limiting the debugging process's flexibility and efficiency.&lt;/p&gt;

&lt;p&gt;This article will use the open-source project AREX as an example to introduce how to implement API debugging functionality in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AREX?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://arextest.com/"&gt;AREX&lt;/a&gt; is an open-source automated regression testing platform based on real requests and data. It utilizes Java Agent technology and comparison techniques to achieve fast and effective regression testing through traffic recording and playback capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 1: Cross-Origin Restrictions
&lt;/h2&gt;

&lt;p&gt;To debug API in the browser, the first challenge to overcome is handling the browser's cross-origin restrictions.&lt;/p&gt;

&lt;p&gt;The browser cross-origin issue refers to the restrictions encountered when using JavaScript code to access resources from one domain's web pages to another domain's resources in web development. Browsers implement a security policy called the Same-Origin Policy to protect user information. This policy is &lt;strong&gt;a browser security feature that restricts how documents and scripts on one origin can interact with resources on another origin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Due to the browser's security policy, HTTP requests cannot be sent from within the browser due to cross-origin restrictions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;p&gt;There are two common solutions to overcome this limitation: Chrome extension proxy and server-side proxy. Now we compare the two methods:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Chrome extension&lt;/th&gt;
&lt;th&gt;Server-side&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;local access&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;speed&lt;/td&gt;
&lt;td&gt;No request time loss&lt;/td&gt;
&lt;td&gt;The overall process speed is influenced by the proxy interface.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Actual request&lt;/td&gt;
&lt;td&gt;The request &lt;code&gt;Origin&lt;/code&gt; will be modified to match the source of the Chrome extension&lt;/td&gt;
&lt;td&gt;the same as the actual request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After weighing the options, AREX chose Chrome extension proxy. &lt;/p&gt;

&lt;p&gt;The background of the Chrome extension has the ability to send cross-domain requests. Then we can intercept the requests received on the browser side and communicate with the background of the Chrome extension using &lt;code&gt;window.postMessage&lt;/code&gt; (where communication also requires the &lt;code&gt;content-script&lt;/code&gt; of a Chrome extension to serve as a data bridge).&lt;/p&gt;

&lt;p&gt;The implementation is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the client-side script:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Generate a random string and store it as a string in the variable &lt;code&gt;tid&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;window.postMessage()&lt;/code&gt; method to send a message to other extensions. The message includes an identifier of type &lt;code&gt;AREX_EXTENSION_REQUEST&lt;/code&gt;, &lt;code&gt;tid&lt;/code&gt;, and the &lt;code&gt;params&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;message&lt;/code&gt; event listener &lt;code&gt;receiveMessage&lt;/code&gt; to receive messages sent by other extensions.&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;receiveMessage&lt;/code&gt; function, check if the received message is of type &lt;code&gt;AREX_EXTENSION_RES&lt;/code&gt; and if the &lt;code&gt;tid&lt;/code&gt; matches the one sent in the previous message. If there is a match, remove the event listener.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In the content script:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Add a &lt;code&gt;message&lt;/code&gt; event listener to receive messages sent from the page script or other extension programs. &lt;/li&gt;
&lt;li&gt;Within the event listener, verify if the received message is of type &lt;code&gt;AREX_EXTENSION_REQUEST&lt;/code&gt;. If so, utilize the &lt;code&gt;chrome.runtime.sendMessage()&lt;/code&gt; method to dispatch the message to the background script.&lt;/li&gt;
&lt;li&gt;After receiving a response from the background script, use the &lt;code&gt;window.postMessage()&lt;/code&gt; method to send the response message back to the page script or other extension programs.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In the background script:&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;code&gt;chrome.runtime.onMessage.addListener()&lt;/code&gt; method to add a listener for receiving messages from content scripts or other extension programs. &lt;/li&gt;
&lt;li&gt;Within the listener, you can handle the received messages and respond accordingly based on your requirements.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// arex&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__AREX_EXTENSION_REQUEST__&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__AREX_EXTENSION_RES__&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// content-script.js&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__AREX_EXTENSION_REQUEST__&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//  communicate with background&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__AREX_EXTENSION_RES__&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;ev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tid&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;// background.js&lt;/span&gt;
&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenge 2: API Debugging
&lt;/h2&gt;

&lt;p&gt;The above has resolved the cross-origin restrictions. The next challenge is how to implement the API debugging feature in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;p&gt;Postman is a mature API debugging tool. By standing on the shoulders of giant, we introduced the JavaScript sandbox of Postman in AREX. We utilize the sandbox to run pre-request scripts, post-request scripts, and assertions for API debugging.&lt;/p&gt;

&lt;p&gt;Here is the flowchart of request in AREX :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcz9s6vag0budaklf03k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcz9s6vag0budaklf03k.png" alt="request flowchart" width="800" height="777"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When sending the request, the data in the form will be gathered together into a data structure as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;export&lt;/span&gt; &lt;span class="nt"&gt;interface&lt;/span&gt; &lt;span class="nt"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;[];&lt;/span&gt;
  &lt;span class="nt"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;[];&lt;/span&gt;
  &lt;span class="nt"&gt;preRequestScript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;testScript&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the data structure of AREX, which will be converted into Postman's. Then, invoke the PostmanRuntime.Runner() method, and pass the converted Postman data structure and the currently selected environment variables. The Runner will execute the &lt;code&gt;preRequestScript&lt;/code&gt; and &lt;code&gt;testScript&lt;/code&gt; . The &lt;code&gt;preRequestScript executes *before* a *request* runs&lt;/code&gt; and can include other requests as well as operations on request parameters and environment variables. The &lt;code&gt;testScript&lt;/code&gt; is after the request and can perform assertions on the response. Additionally, the scripts can use &lt;code&gt;console.log&lt;/code&gt; to output data for debugging in the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var runner = new runtime.Runner(); // runtime = require('postman-runtime');

// A standard Postman collection object
var collection = new sdk.Collection();

runner.run(collection, {}, function (err, run) {
    run.start({
      assertion:function (){},
      prerequest:function (){}, 
      test:function (){}, 
      response:function (){} 
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cross-origin issues also exist in the Postman sandbox. Due to the high integration of the Postman sandbox,  we have adopted Ajax (Asynchronous JavaScript and XML) interception technique to ensure convenience and synchronization with &lt;code&gt;PostmanRuntime&lt;/code&gt;. By intercepting Ajax requests on the browser side, we can modify requests, add custom logic, or perform other processing operations.&lt;/p&gt;

&lt;p&gt;When the Postman sandbox sends a request, it carries a request header "postman-token". After intercepting Ajax's request, we assemble the request parameters and send them to the browser extension via &lt;code&gt;window.postMessage&lt;/code&gt;. The browser extension then constructs a fetch request and returns the data to the Postman sandbox. Then Postman sandbox outputs the final results, including the response, test results, and &lt;code&gt;console.log&lt;/code&gt;. The responseType should be specified as "arraybuffer".&lt;/p&gt;

&lt;p&gt;The specific process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register a request handler using the &lt;code&gt;xspy.onRequest()&lt;/code&gt; method. This handler accepts two parameters: &lt;code&gt;request&lt;/code&gt; and &lt;code&gt;sendResponse&lt;/code&gt;. The &lt;code&gt;request&lt;/code&gt; parameter contains relevant information about the request, such as the method, URL, headers, request body, etc. &lt;code&gt;sendResponse&lt;/code&gt; is a callback function used to send a response back to the requester. &lt;/li&gt;
&lt;li&gt;In the handler, check if there is &lt;code&gt;postman-token&lt;/code&gt; in the request headers to determine if the request is from Postman.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If the request is from Postman, use &lt;code&gt;AgentAxios&lt;/code&gt; to send a new request with the same details. The response is stored in &lt;code&gt;agentData&lt;/code&gt;. Create a &lt;code&gt;dummyResponse&lt;/code&gt; object with relevant information from the original request. Set the status, headers, ajaxType, responseType, and response fields of &lt;code&gt;dummyResponse&lt;/code&gt; based on &lt;code&gt;agentData&lt;/code&gt;. Finally, return the dummyResponse using &lt;code&gt;sendResponse()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not, then call &lt;code&gt;sendResponse()&lt;/code&gt; directly, indicating that no response should be returned.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;xspy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// check if is sent from postman&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postman-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;agentData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nc"&gt;AgentAxios&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="p"&gt;});&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dummyResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agentData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;agentData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;ajaxType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xhr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;responseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;arraybuffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;agentData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt;  &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dummyResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nf"&gt;sendResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenge 3: Binary serialization
&lt;/h2&gt;

&lt;p&gt;For requests with data formats of &lt;code&gt;x-www-form-urlencoded&lt;/code&gt; and &lt;code&gt;Raw&lt;/code&gt;, handling them is relatively easy since they are regular JSON objects. However, for requests of &lt;code&gt;form-data&lt;/code&gt; and &lt;code&gt;binary&lt;/code&gt;, they need support for sending binary payload. Unfortunately, the &lt;code&gt;postMessage&lt;/code&gt; communication method in Chrome extensions does not support directly passing binary files, which makes it impossible to handle these two types of requests directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;To address this issue, AREX utilizes the base64 encoding technique. When a user selects a file, AREX converts the binary file into a base64 string for transmission. On the Chrome extension side, AREX decodes the base64 data and uses it to construct the actual &lt;code&gt;fetch&lt;/code&gt; request. This approach bypasses the limitation of directly passing binary files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h1pe852ra9e8exhyxih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h1pe852ra9e8exhyxih.png" alt="Image description" width="512" height="1164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This flowchart describes the process of converting binary files in FormData to Base64 strings, and then converting them back to files and further processing them through the Chrome proxy extension.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;form-data binary: a FormData contains a binary file. &lt;/li&gt;
&lt;li&gt;FileReader: Uses a FileReader object to read the binary file. &lt;/li&gt;
&lt;li&gt;readAsDataURL base64 string: The FileReader uses the &lt;code&gt;readAsDataURL&lt;/code&gt; method to read the binary file as a Base64 string. &lt;/li&gt;
&lt;li&gt;Chrome extension proxy: After the Base64 string is read, it is passed to the Chrome proxy extension. &lt;/li&gt;
&lt;li&gt;base64 string: Represents the Base64 string obtained after the binary file is read by the FileReader. &lt;/li&gt;
&lt;li&gt;Uint8Array: In the Chrome extension proxy, the Base64 string is converted to a Uint8Array. &lt;/li&gt;
&lt;li&gt;File: A new File object is created using the data from the Uint8Array. &lt;/li&gt;
&lt;li&gt;fetch: The new File object is further processed using the fetch method or other means, such as uploading to a server or performing other operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Code Analysis
&lt;/h3&gt;

&lt;p&gt;Now let's look at the code level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;File&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;Base64&lt;/span&gt;
&lt;span class="err"&gt;const&lt;/span&gt; &lt;span class="py"&gt;toBase64&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;(file: File): Promise&amp;lt;string&amp;gt; =&amp;gt;&lt;/span&gt;
 &lt;span class="err"&gt; new&lt;/span&gt; &lt;span class="err"&gt;Promise((resolve,&lt;/span&gt; &lt;span class="err"&gt;reject)&lt;/span&gt; &lt;span class="err"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; const&lt;/span&gt; &lt;span class="py"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;new FileReader();&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; reader.readAsDataURL(file)&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="py"&gt;reader.onload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;() =&amp;gt; resolve(reader.result as string);&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="py"&gt;reader.onerror&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;reject;&lt;/span&gt;
  &lt;span class="err"&gt;})&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Base64&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;File&lt;/span&gt;
&lt;span class="err"&gt;function&lt;/span&gt; &lt;span class="err"&gt;base64ToFile(dataurl:&lt;/span&gt; &lt;span class="err"&gt;string,&lt;/span&gt; &lt;span class="err"&gt;filename:&lt;/span&gt; &lt;span class="err"&gt;string)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; const&lt;/span&gt; &lt;span class="py"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dataurl.split(',') || [''],&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="py"&gt;mime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;arr[0].match(/:(.*?);/)?.[1],&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="py"&gt;bstr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;atob(arr[1]);&lt;/span&gt;
 &lt;span class="err"&gt; let&lt;/span&gt; &lt;span class="py"&gt;n&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;bstr.length;&lt;/span&gt;
 &lt;span class="err"&gt; const&lt;/span&gt; &lt;span class="py"&gt;u8arr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;new Uint8Array(n);&lt;/span&gt;
 &lt;span class="err"&gt; while&lt;/span&gt; &lt;span class="err"&gt;(n--)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; u8arr&lt;/span&gt;&lt;span class="nn"&gt;[n]&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="err"&gt;bstr.charCodeAt(n)&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;  &lt;span class="err"&gt;}&lt;/span&gt;
 &lt;span class="err"&gt; return&lt;/span&gt; &lt;span class="err"&gt;new&lt;/span&gt; &lt;span class="err"&gt;File(&lt;/span&gt;&lt;span class="nn"&gt;[u8arr]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="err"&gt;filename,&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;type:&lt;/span&gt; &lt;span class="err"&gt;mime&lt;/span&gt; &lt;span class="err"&gt;})&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;​&lt;/span&gt;
&lt;span class="err"&gt;export&lt;/span&gt; &lt;span class="err"&gt;default&lt;/span&gt; &lt;span class="err"&gt;base64ToFile&lt;/span&gt;&lt;span class="c"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  File to Base64
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;toBase64&lt;/code&gt; function takes a &lt;code&gt;File&lt;/code&gt; object as input and returns a &lt;code&gt;Promise&lt;/code&gt; that resolves to a Base64 string representing the file.&lt;/p&gt;

&lt;p&gt;Inside the function, a &lt;code&gt;FileReader&lt;/code&gt; object is used to read the contents of the &lt;code&gt;File&lt;/code&gt; object as a Data URL by calling &lt;code&gt;reader.readAsDataURL(file)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;onload&lt;/code&gt; event handler is set on the &lt;code&gt;FileReader&lt;/code&gt; object, which will be triggered when the file reading operation is complete. Inside the event handler, the &lt;code&gt;reader.result&lt;/code&gt; property, which contains the result of the file reading operation, is resolved as a &lt;code&gt;string&lt;/code&gt; and passed to the &lt;code&gt;resolve&lt;/code&gt; function of the &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If an error occurs during the file reading operation, the &lt;code&gt;onerror&lt;/code&gt; event handler is triggered, and the &lt;code&gt;reject&lt;/code&gt; function of the &lt;code&gt;Promise&lt;/code&gt; is called with the error.&lt;/p&gt;

&lt;h4&gt;
  
  
  Base64 to File
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;The function base64ToFile&lt;/code&gt; takes two parameters: &lt;code&gt;dataurl&lt;/code&gt; (a Base64 string) and &lt;code&gt;filename&lt;/code&gt; (the name of the file). The function returns a &lt;code&gt;File&lt;/code&gt; object that represents the file with the specified name and content.&lt;/p&gt;

&lt;p&gt;Inside the function, the Base64 string is split into an array using the comma separator. The first element of the array contains the MIME type of the file, which is extracted using a regular expression. The second element of the array contains the Base64-encoded content of the file, which is decoded using the &lt;code&gt;atob&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The decoded content is then converted to a &lt;code&gt;Uint8Array&lt;/code&gt; object, which is used to create a new &lt;code&gt;File&lt;/code&gt; object. The &lt;code&gt;File&lt;/code&gt; object is returned with the specified filename and MIME type.&lt;/p&gt;

&lt;p&gt;The above is the internal mechanism of the entire AREX debugging process.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on &lt;a href="https://twitter.com/AREX_Test"&gt;Twitter&lt;/a&gt;&lt;br&gt;
📝 Join AREX &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Slack&lt;/a&gt;&lt;br&gt;
📧 Join the &lt;a href="https://groups.google.com/g/arex-test"&gt;Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Production Traffic Replication with Java Agent</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Mon, 09 Oct 2023 09:09:05 +0000</pubDate>
      <link>https://dev.to/arex_test/a-deep-dive-into-java-agent-implementation-of-production-traffic-replication-1563</link>
      <guid>https://dev.to/arex_test/a-deep-dive-into-java-agent-implementation-of-production-traffic-replication-1563</guid>
      <description>&lt;p&gt;Production traffic replication is a technique used to record traffic in production environment, and replaying it in another. It is also considered the best solution for performing regression testing. &lt;/p&gt;

&lt;h2&gt;
  
  
  Record and playback testing tools
&lt;/h2&gt;

&lt;p&gt;Depending on the location where the recording takes place, all the recording tools can be categorized as: &lt;strong&gt;web server-based recording&lt;/strong&gt;, &lt;strong&gt;application layer-based recording&lt;/strong&gt;, and &lt;strong&gt;network protocol stack-based recording&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsk00loiww6emeox3kxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsk00loiww6emeox3kxl.png" alt="Tool Classification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Web server-based recording
&lt;/h3&gt;

&lt;p&gt;HTTP request recording based on web server refers to the process of recording and replicating the requests and responses between the web server and client. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;: Supports a diverse range of request formats and protocols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;: High maintenance cost, consumes a significant amount of online resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Internet protocol stack-based recording
&lt;/h3&gt;

&lt;p&gt;Directly monitoring to network ports and recording by duplicating data packets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;: Low impact on the application&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;: More low-level implementation, higher cost. In addition, it cannot be used to test non-idempotent interfaces, because traffic replay can result in the generation of invalid or dirty data, potentially impacting the correctness of the business operations.&lt;/p&gt;

&lt;p&gt;Representative tools: goReplay, tcpCopy, tcpReplay&lt;/p&gt;

&lt;h3&gt;
  
  
  Application layer-based recording
&lt;/h3&gt;

&lt;p&gt;Since Spring Boot is widely used as a Java backend framework, we can leverage AOP (Aspect-Oriented Programming) to intercept the Controller layer and record traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;: Non-intrusive to the code, relatively quick and simple implementation. In addition to validating the returned responses from server, it also supports validation of data written to the service, validation of database, message queue, Redis data, and even validation of runtime in-memory data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;: May consume some online resources, potential impact on business&lt;/p&gt;

&lt;p&gt;Representative tools: ngx_http_mirror_module, Java sandbox, AREX&lt;/p&gt;

&lt;p&gt;AREX is an automated regression testing platform based on traffic recording and playback. It utilizes Java Agent and bytecode enhancement technology to capture the entry points of real request chains and the request and response data of their 3rd-party dependencies in the production environment. Then, in the testing environment, it simulates to replay these requests and verifies the correctness of the entire invocation chain logic one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How dose AREX Java Agent work?
&lt;/h2&gt;

&lt;p&gt;AOP (Aspect-Oriented Programming) is a programming paradigm that provides a way to modularize cross-cutting concerns, such as logging, security, and transaction management. It achieves this by separating these concerns from the core business logic, allowing you to add extra functionality to existing modules without directly modifying their code.&lt;/p&gt;

&lt;p&gt;Java Agent is a mechanism in Java that allows you to dynamically modify the bytecode of classes at runtime. It provides a way to instrument and manipulate the behavior of Java applications, including adding aspects or intercepting method calls.&lt;/p&gt;

&lt;p&gt;In the context of AOP, a Java Agent can be used to apply AOP principles by intercepting method calls and weaving in additional behavior. It provides the necessary infrastructure to implement AOP in Java applications.&lt;/p&gt;

&lt;p&gt;As shown in the figure below, a request typically has a chain of calls consisting of an entry point and dependencies that are either synchronous or asynchronous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzanii7ezkwdnt5cydnyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzanii7ezkwdnt5cydnyz.png" alt="Java Agent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;recording&lt;/strong&gt; process is to connect the entry and dependency calls through a &lt;code&gt;RecordId&lt;/code&gt; to form a complete test case. AREX-Agent enhances the bytecode of the entry and dependency calls, intercepts the call process when the code is executed, and records the entry parameter, return value, and exceptions of the call, and sends them to the storage service.&lt;/p&gt;

&lt;p&gt;During &lt;strong&gt;playback&lt;/strong&gt; in the test environment, the AREX Agent simulates requests using the real data recorded in the production environment. The AREX Agent determines whether playback is required by identifying the playback flag. If playback is required, the actual method invocation is not performed. Instead, the stored response data from the storage service is retrieved and returned as the response.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

public Integer parseIp(String ip) {
    if (needReplay()) {
        //  In the playback scenario, the collected data is used as the return result, which is also known as Mocking.
        return DataService.query("parseIp", ip);
    }

    int result = 0;
    if (checkFormat(ip)) {
        String[] ipArray = ip.split("\\.");
        for (int i = 0; i &amp;lt; ipArray.length; i++) {
            result = result &amp;lt;&amp;lt; 8;
            result += Integer.parseInt(ipArray[i]);
        }
    }

    if (needRecord()) {
        // In the recording scenario, the parameters and execution results are saved into the database.
        DataService.save("pareseIp", ip, result);
    }
    return result;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Taking the above function as an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8sbh3kl1mrd1nr1wh28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8sbh3kl1mrd1nr1wh28.png" alt="function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the beginning of the function, a decision is made whether to playback or not. If playback is necessary, the collected data is utilized as the return result, which is commonly referred to as Mocking.&lt;/p&gt;

&lt;p&gt;At the end of the function, a decision is made whether to record or not. If recording is necessary, the intermediate data that the application needs to store is saved to the AREX database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical challenges
&lt;/h2&gt;

&lt;p&gt;The process of recording and replay is very complex. Next, we will dive into the challenges and technical details of AREX Agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  ClassLoader Isolation
&lt;/h3&gt;

&lt;p&gt;To ensure the AREX Agent code and dependencies do not have a conflict with the application code, the AREX Agent and application code are isolated by different class loaders. As shown in the figure below, AREX Agent overrides the &lt;code&gt;findClass&lt;/code&gt; method by customizing &lt;code&gt;AgentClassLoader&lt;/code&gt; to ensure that the classes used by AREX Agent will only be loaded by &lt;code&gt;AgentClassLoader&lt;/code&gt;, so as to avoid conflicts with the application &lt;code&gt;ClassLoader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Meanwhile, in order to let the application &lt;code&gt;ClassLoader&lt;/code&gt; recognize the recording and playback code of AREX Agent, AREX Agent injects the byte code needed for recording and playback into the application &lt;code&gt;ClassLoader&lt;/code&gt; through the ByteBuddy ClassInjector to make sure there is no &lt;code&gt;ClassNotFoundException/NoClassDefFoundError&lt;/code&gt; during recording and replay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx16484v57tmfefcemx4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx16484v57tmfefcemx4t.png" alt="ClassLoader Isolation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracing
&lt;/h3&gt;

&lt;p&gt;When the data is recorded and replayed, the entry point of a request and the calls of each dependency will be linked together by a RecordId. When dealing with multi-threading and various asynchronous frameworks, there are significant challenges in maintaining data continuity. AREX Agent addresses the issue of passing RecordId across threads by enhancing the thread behavior, ensuring seamless transfer of RecordId between threads. The supported threads and thread pools are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread&lt;/li&gt;
&lt;li&gt;ThreadPoolExecutor&lt;/li&gt;
&lt;li&gt;ForkJoinTask&lt;/li&gt;
&lt;li&gt;FutureTask&lt;/li&gt;
&lt;li&gt;FutureCallback&lt;/li&gt;
&lt;li&gt;Reactor Framework&lt;/li&gt;
&lt;li&gt;……&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a simple example for better understanding of implementation.&lt;/p&gt;

&lt;p&gt;When invoking &lt;code&gt;java.util.concurrent.ThreadPoolExecutor#execute(Runnable runnable)&lt;/code&gt;, the parameter &lt;code&gt;AgentRunnableWrapper&lt;/code&gt; is used to wrap the &lt;code&gt;AgentRunnableWrapper runnable&lt;/code&gt;. When constructing &lt;code&gt;AgentRunnableWrapper&lt;/code&gt;, the current thread context is captured. In the &lt;code&gt;run&lt;/code&gt; method, the subthread context is replaced, and after execution, the child thread context is restored. Here is an example code snippet:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

executors.execute(Runnable runnable)
executors.submit(Callable callable)

public void execute(Runnable var1) {
var1 =RunnableWrapper.wrap(var1);
}

public class RunnableWrapper implements Runnable {
  private final Runnable runnable;
  private final TraceTransmitter traceTransmitter;

  private RunnableWrapper(Runnable runnable){
    this.runnable = runnable;
    //Capture the current thread context 
    this.traceTransmitter = TraceTransmitter.create();
    }

  @Override
  public void run(){
    //Replacing the subthread context 
    try (TraceTransmitter tm = traceTransmitter.transmit()){
      (runnable.run();
    }
    //Reducing the Atomic Thread Context 
  }
}

...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Component Version Compatibility
&lt;/h3&gt;

&lt;p&gt;The components introduced by an application may have multiple versions, and different versions of the same component may be incompatible, such as changes in package, addition or removal of methods, etc. In order to support multiple versions of components, AREX Agent needs to identify the correct component version for bytecode enhancement, to avoid duplicate enhancement or enhancement of the wrong version.&lt;/p&gt;

&lt;p&gt;AREX Agent identifies the Name and Version in the &lt;code&gt;META-INF/MANIFEST.MF&lt;/code&gt; of the component JAR file, and matches the version during class loading to ensure that the correct version is used for bytecode enhancement.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mock Local Cache
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Object value = localCache.get(key)
// Cache is available during recording, but not available during playback in which case the code needs to query the database (db.query()).
if (value != null) {
    return value;
} else {
    return db.query();
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As shown above, during recording, the code first attempts to retrieve the value associated with the given key from the local cache (&lt;code&gt;localCache.get(key)&lt;/code&gt;). If the value is not null, it means that the corresponding data is available in the cache during recording, and it is directly returned.&lt;/p&gt;

&lt;p&gt;However, during playback, the cache is not available. Therefore, if the value retrieved from the cache is null, it means that the data is not present in the cache during playback. In this case, the code needs to query the database (&lt;code&gt;db.query()&lt;/code&gt;) to retrieve the data and return it as the result. &lt;/p&gt;

&lt;p&gt;In a word, the execution flow of the replay request is often different from the recording due to inconsistent local cache data with the recording, resulting a low pass rate of replay testing. There are a few challenges to solve this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is challenging to achieve real-time synchronization between production and test cache data due to the isolation between them.&lt;/li&gt;
&lt;li&gt;Local memory is implemented in various ways, and it is impossible to perceive each one individually.&lt;/li&gt;
&lt;li&gt;Local memory data is typically fundamental data and can have a large volume. Recording this data can lead to significant performance overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, the solution adopted by AREX Agent is to only record the cache data used in the current request chain. This is achieved by allowing the application to configure dynamic classes to identify the recording, and during playback in the test environment, the cache data is automatically replaced to ensure consistency between the recorded and playback memory data. We are still researching the solution of recording large cache data. &lt;/p&gt;

&lt;h3&gt;
  
  
  Time Mock
&lt;/h3&gt;

&lt;p&gt;Many business systems are time-sensitive, where accessing them at different times can result in different outcomes. If the recording and playback times are inconsistent, it can lead to playback failures. Additionally, modifying the machine time on the test server is not suitable as playback requests are concurrent, and many servers do not allow modification of the current time. Therefore, we need to implement mocking of the current time at the code level to address this issue.&lt;/p&gt;

&lt;p&gt;The currently supported time types are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;java.time.Instant&lt;/li&gt;
&lt;li&gt;java.time.LocalDate&lt;/li&gt;
&lt;li&gt;java.time.LocalTime&lt;/li&gt;
&lt;li&gt;java.time.LocalDateTime&lt;/li&gt;
&lt;li&gt;java.util.Date&lt;/li&gt;
&lt;li&gt;java.util.Calendar&lt;/li&gt;
&lt;li&gt;org.joda.time.DateTimeUtils&lt;/li&gt;
&lt;li&gt;java.time.ZonedDateTime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;public static native long currentTimeMillis()&lt;/code&gt; is an intrinsic function. When the JVM performs inline optimization on intrinsic functions, it replaces the existing bytecode with internal code (JIT), which causes the enhanced code by AREX Agent to become ineffective. The JDK performs inline operations on &lt;code&gt;System.currentTimeMillis() and System.nanoTime()&lt;/code&gt; as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// // https://hg.openjdk.org/jdk8u/jdk8u/hotspot/file/dae2d83e0ec2/src/share/vm/classfile/vmSymbols.hpp#l631
//------------------------inline_native_time_funcs--------------
// inline code for System.currentTimeMillis() and System.nanoTime()
// these have the same type and signature
bool LibraryCallKit::inline_native_time_funcs(address funcAddr, const char* funcName) {
  const TypeFunc* tf = OptoRuntime::void_long_Type();
  const TypePtr* no_memory_effects = NULL;
  Node* time = make_runtime_call(RC_LEAF, tf, funcAddr, funcName, no_memory_effects);
  Node* value = _gvn.transform(new ProjNode(time, TypeFunc::Parms+0));
#ifdef ASSERT
  Node* value_top = _gvn.transform(new ProjNode(time, TypeFunc::Parms+1));
  assert(value_top == top(), "second value must be top");
#endif
  set_result(value);
  return true;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;AREX Agent has taken special care of this issue by replacing the code that uses the method &lt;code&gt;System.currentTimeMillis()&lt;/code&gt; with AREX Agent's method of obtaining the time directly through the application configuration, avoiding inline optimizations.&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 &lt;a href="https://twitter.com/AREX_Test" rel="noopener noreferrer"&gt;Follow us on Twitter&lt;/a&gt;&lt;br&gt;
📝 &lt;a href="https://arexcommunity.slack.com/ssb/redirect" rel="noopener noreferrer"&gt;Join AREX Slack&lt;/a&gt;&lt;br&gt;
📧 &lt;a href="https://groups.google.com/g/arex-test" rel="noopener noreferrer"&gt;Join the Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>java</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Coolest Record and Playback Testing tool to start automating tests</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Wed, 27 Sep 2023 06:33:49 +0000</pubDate>
      <link>https://dev.to/arex_test/the-coolest-record-and-playback-testing-tool-to-start-automating-tests-32in</link>
      <guid>https://dev.to/arex_test/the-coolest-record-and-playback-testing-tool-to-start-automating-tests-32in</guid>
      <description>&lt;h2&gt;
  
  
  What Is Record and Playback Testing
&lt;/h2&gt;

&lt;p&gt;Record and playback testing is regarded as a low-code method that automates software &lt;strong&gt;testing without writing test scripts&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When you manually send requests on an application under test (AUT) in the production environment, such as a web application, the tool will capture these requests and automatically converts them into test cases.&lt;/p&gt;

&lt;p&gt;You can then “replay” these requests in test environment to make sure that they function correctly after any changes have been made to the software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Record and Playback Testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Record and playback testing &lt;strong&gt;does not require knowledge of coding&lt;/strong&gt; or the ability to execute programming languages, making it accessible to users of all skill levels. Even individuals with no programming background, such as product managers, designers, marketers, and others, can participate in.&lt;/li&gt;
&lt;li&gt;It’s the easiest way for regression testing as it &lt;strong&gt;greatly reduces the time and effort&lt;/strong&gt; needed to create test scripts. By recording requests as test cases, there may be no need to write or maintain a regression suite.&lt;/li&gt;
&lt;li&gt;Replaying online traffic can &lt;strong&gt;perfectly simulate real user behavior&lt;/strong&gt;, avoiding the discrepancies that may happen in manual writing test cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges of Record and Playback Testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited Functionality&lt;/strong&gt; – Most record and playback testing tools are unable to verify whether the functionality is correctly executed. They can only reuse recorded steps to create another test case for playback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Troubleshooting&lt;/strong&gt; – Once a test played, if it failed, there was little information to help QA determine why it failed. Was it poorly written? Did it fail because of an element loaded slowly? Or was there actually a bug in the application?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance&lt;/strong&gt; – The recorded tests might easily become broken when your application changes frequently. Then you need to re-record the tests or update the test scripts manually, dropping your testing efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restrictive&lt;/strong&gt; - The capabilities of most record and playback testing tools are limited to recording tests while performing manual actions on web pages. However, in reality, testing software involves more than just operations on a website.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the right testing tool
&lt;/h2&gt;

&lt;p&gt;Over the years, the record and playback testing method has not been widely used due to the failure of many tools to address the aforementioned challenges.&lt;/p&gt;

&lt;p&gt;Despite facing initial challenges, record and playback testing has evolved into a more advanced and intelligent application testing tool with the help of modern tools.&lt;/p&gt;

&lt;p&gt;AREX is an open-source automation test platform. It record real requests in the production environment and mock all requests to third-party dependencies with Java Agent bytecode injection technology, then replay the request and mock data in the test environment. Furthermore, it compares the recorded and replayed response messages, and lists the differences, allowing developers and testers to quickly troubleshoot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2tssrqUz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://arextest.com/assets/images/c1.multi.service-ddd6795e7a6bd8f1a3f0b37ca6a33c51.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2tssrqUz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://arextest.com/assets/images/c1.multi.service-ddd6795e7a6bd8f1a3f0b37ca6a33c51.png" alt="architecture diagram" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With AREX, you can accelerate your testing processes and identify defects and errors earlier in the development cycle, which can ultimately save time and resources. As an open source tool, AREX is constantly evolving and improving thanks to contributions from its community of users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PM44p1hL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://arextest.com/img/feature-collapse/replay_report-light-en.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PM44p1hL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://arextest.com/img/feature-collapse/replay_report-light-en.png" alt="feature-img" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Low Cost
&lt;/h4&gt;

&lt;p&gt;Low integration cost. No need to write test cases, while massive online requests can also ensure high coverage. Instrumentation code is simple enough for low performance loss.&lt;/p&gt;

&lt;h4&gt;
  
  
  Diversity validation&lt;a href="http://arextest.com/docs/intro#diversity-support"&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;In addition to validating the returned responses from server, it also supports validation of data written to the service, validation of database, message queue, Redis data, and even validation of runtime in-memory data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r63ft6k6bj4onb7g8kj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r63ft6k6bj4onb7g8kj.png" alt="Diversity validation" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Intelligent Mock
&lt;/h4&gt;

&lt;p&gt;Supports mocking for multiple popular technical frameworks, as well as local time and cache. This ensures accurate restoration of the production data environment during playback in testing environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhaewcjdhyagxsg5y3y8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhaewcjdhyagxsg5y3y8.png" alt="supported frameworks" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  One-click debugging online problems in your local environment.
&lt;/h4&gt;

&lt;p&gt;Due to configuration and data differences between the online and local environments, it is often challenging to reproduce online issues quickly in a local testing environment, making troubleshooting more difficult. With AREX, as all third-party dependencies can be mocked, it becomes convenient to debug locally.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkn9r6f7yk0y8t7rc3k6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkn9r6f7yk0y8t7rc3k6.png" alt="debug" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Safe and stable
&lt;/h4&gt;

&lt;p&gt;Safe: Desensitize the data for some sensitive information to protect of sensitive private data when recording the real traffic in the production environment.&lt;/p&gt;

&lt;p&gt;Stable: Code isolation. Automatically reduce or turn off data collection during high system activity, which ensures that system stability is not affected.&lt;/p&gt;

&lt;p&gt;Try AREX: &lt;a href="http://arextest.com/docs/chapter1/quick-installation"&gt;http://arextest.com/docs/chapter1/quick-installation&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Community⤵️&lt;br&gt;
🐦 Follow us on Twitter&lt;br&gt;
📝 Join AREX Slack&lt;br&gt;
📧 Join the Mailing List&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Complete Guide of Regression Testing: Definition, Tools &amp; How to do</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 21 Sep 2023 07:21:08 +0000</pubDate>
      <link>https://dev.to/arex_test/complete-guide-of-regression-testing-definition-tools-how-to-do-4pki</link>
      <guid>https://dev.to/arex_test/complete-guide-of-regression-testing-definition-tools-how-to-do-4pki</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftai3txejdhw9u9thuqz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftai3txejdhw9u9thuqz6.png" alt="testing report" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is regression testing?
&lt;/h2&gt;

&lt;p&gt;Regression testing refers to testing an application to ensure that the previously code remains operational as expected after changes or enhancements made to it. It is a quality control measure that helps developers and testers identify potential defects or problems that may occur with new code additions, design modifications, or system upgrades before a new feature markets.&lt;/p&gt;

&lt;p&gt;It should be noted that regression testing is more than just simply re-testing. Re-testing concentrates solely on the test cases that have failed, whereas regression testing is used to examine the test cases that have passed to identify any unanticipated new bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to perform regression testing?
&lt;/h2&gt;

&lt;p&gt;Typically, regression testing is performed after any changes have been made to the software to verify the changes have not introduced any new issues or affected any previously working functionality, like the following scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After making changes to the software.&lt;/li&gt;
&lt;li&gt;After fixing a defect.&lt;/li&gt;
&lt;li&gt;After updating to the operating system, browser, or other external applications occur that may affect product functionality.&lt;/li&gt;
&lt;li&gt;After a major release.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to perform Regression Testing?
&lt;/h2&gt;

&lt;p&gt;Regression testing can be executed both manually and in an automated manner. Test Engineers primarily use special techniques and methods to perform Regression tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxk0uik1ijhit1gplv8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faxk0uik1ijhit1gplv8q.png" alt="regression testing" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the changes&lt;/strong&gt;: Identify the changes made to the system under test, such as code modifications, enhancements, bug fixes, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select the test cases:&lt;/strong&gt; Select the test cases that cover the modified or impacted functionalities by analyzing the requirements and design documents, test cases, and bug reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize the test cases:&lt;/strong&gt; Prioritize the selected test cases based on their criticality and impact on the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a regression test suite:&lt;/strong&gt; Create a regression test suite that includes the selected test cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute the test cases:&lt;/strong&gt; Execute the test cases in the regression test suite and compare the actual results with the expected results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report the defects:&lt;/strong&gt; Report any defects identified during the test execution and ensure that they are fixed by the development team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat the cycle:&lt;/strong&gt; Repeat the above steps whenever there are new changes made to the system under test to ensure that the system is functioning as expected.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Challenges of regression testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Large Scope and Coverage &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are challenges faced while listing out functionalities to be included in the test suite and possible scenarios. Regression testing may have limited test coverage, as it may not be possible to test all possible scenarios due to time and resource constraints.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintenance and Optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maintaining and optimizing the existing regression test suites is a significant task. When there are new modifications, the current regression test suites may need to be modified by adding, eliminating, or altering the existing test cases. All of this must be completed before the regression testing deadline. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate database, cache, or message queue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To validate business correctness, it is not sufficient to simply validate the returned results from server. In some scenarios such as generating orders and making payments that require writing data to the database, message queues, cache, etc., it is necessary to validate whether the interaction requests between the business system and the underlying business system have changed, whether the content of the data written to the database by the business system is correct, whether the content of the asynchronous messages sent by the business system is correct, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the right testing tool
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to overcome the challenges in regression testing. One of the most straightforward approaches is &lt;strong&gt;record and replay testing&lt;/strong&gt;. This low-code method involves using a tool to automate tests without writing test scripts. As a result, it is often considered the simplest way to perform regression testing.&lt;/p&gt;

&lt;p&gt;There are different tools and frameworks that can be used to perform Regression Testing. Such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Selenium IDE: Selenium IDE is a user-friendly and free tool for recording and playing back web application tests. With its simple installation process and browser extension, you can quickly start recording your testing activities. However, a significant drawback is that the tool has limited capabilities in terms of scalability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TestComplete: TestComplete is another pre-built tool that includes a record and replay functionality, as well as scripting capabilities and advanced features such as parallel or keyword-driven testing, object recognition engine, and robust reporting. These features can help your team enhance its testing capabilities and improve overall efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Katalon: Tthe record and replay feature is readily available and easy to use, with a simple interface. The tool also includes a built-in object repository that follows the Page-Object model, making it easy to organize and maintain test objects after recording tests. You can start utilizing this feature immediately without any additional setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AREX: It is an open-source automated regression testing platform, which allows one to record production traffic and replay it to testing environment. By comparing the recorded responses against replay, it can clarify the impact of code changes to realize efficient automated regression testing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Regression testing with AREX
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Record production traffic as test cases
&lt;/h3&gt;

&lt;p&gt;Step 1: Install AREX&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/arextest/deployments.git
&lt;span class="nb"&gt;cd &lt;/span&gt;deployments
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Modify the way your production environment for the application under test runs to the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-javaagent&lt;/span&gt;:/path/to/arex-agent-0.1.0.jar &lt;span class="nt"&gt;-Darex&lt;/span&gt;.service.name&lt;span class="o"&gt;=&lt;/span&gt;your-service-name &lt;span class="nt"&gt;-Darex&lt;/span&gt;.storage.service.host&lt;span class="o"&gt;=[&lt;/span&gt;storage.service.host:port]&lt;span class="o"&gt;(&lt;/span&gt;storage.service.host:port&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-jar&lt;/span&gt; your-application.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/path/to/arex-agent-0.1.0.jar&lt;/code&gt; needs to be changed to the actual path of the arex-agent-0.1.0.jar file in your server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;your-service-name&lt;/code&gt; needs to be changed to the name of your application under test&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[storage.service.host:port]&lt;/code&gt; needs to be changed to the address and port number of the data storage service&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;your-application.jar&lt;/code&gt; needs to be changed to the jar package name of the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or change the environment variables in the environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export JAVA_OPTS="-javaagent:/path/to/arex-agent-0.1.0.jar -Darex.config.path=/path/to/arex.agent.conf"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Replay traffic in the testing environment
&lt;/h3&gt;

&lt;p&gt;Step 3: Visit the AREX UI from browser, and in the &lt;strong&gt;Replay&lt;/strong&gt; tab, select the service with AREX Agent(i.e. &lt;code&gt;your-service-name&lt;/code&gt; above).&lt;/p&gt;

&lt;p&gt;Step 4: Select Replay, enter the &lt;strong&gt;Target Host&lt;/strong&gt; of your test environment and start replaying all the recorded test cases.&lt;/p&gt;

&lt;p&gt;After the replay is completed, a detailed replay report will be generated, where you can visually see the pass rate, API pass rate, and the details under each API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftai3txejdhw9u9thuqz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftai3txejdhw9u9thuqz6.png" alt="testing report" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the failed API to see the comparison results, and AREX visualized the comparison results to facilitate troubleshooting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzt73oeoyz79a5n6cz8go.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzt73oeoyz79a5n6cz8go.png" alt="diff" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfhhw6i4roz28byt2iq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfhhw6i4roz28byt2iq9.png" alt="diff" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A Comprehensive Guide of API Testing with AREX</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Mon, 18 Sep 2023 08:15:20 +0000</pubDate>
      <link>https://dev.to/arex_test/a-comprehensive-guide-of-api-testing-with-arex-22d</link>
      <guid>https://dev.to/arex_test/a-comprehensive-guide-of-api-testing-with-arex-22d</guid>
      <description>&lt;h3&gt;
  
  
  What is API?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;API&lt;/strong&gt; stands for &lt;strong&gt;Application Programming Interface&lt;/strong&gt;. It’s a connection between different systems or applications that enables them to communicate with each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gFVHNazR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2Ar-_baSCy2PPJYoipn09b6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gFVHNazR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2Ar-_baSCy2PPJYoipn09b6w.png" alt="img" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like ordering food at a restaurant. You only need to tell the waiter what you want, then the waiter will report it to chef and delivers the meal from kitchen. In this process, the waiter plays the role of an API. Similarly, when you use an API, you only need to call the required functions and services, without needing to know the underlying code implementation. Therefore, an API acts as a “middleman” between different applications, allowing them to communicate and interact with each other.&lt;/p&gt;

&lt;p&gt;In contrast to monolithic architectures, microservice is favored by more developers due to the highly flexibility. It promote the decompositon of the application into smaller, isolated services, which allows for each core function within an application to exist independently.&lt;/p&gt;

&lt;p&gt;The emergence of microservice architecture has also led to a surge in the number of APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why API Testing?
&lt;/h3&gt;

&lt;p&gt;With the explosion growth of APIs, the quality of APIs becomes increasingly important. Once an API breaks down, the complete application and user experience are put at risk. Only proper API testing can secure the system from such downtime possibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API testing&lt;/strong&gt; involves testing the collection of APIs and checking if they meet expectations for functionality, reliability, performance, and security and returns the correct response. It helps developers detect and fix potential problems before deploying code to the production environment, thereby improving the availability and reliability of the entire system. Especially in microservice architecture, the applications always undergo frequent changes and updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Protocol
&lt;/h3&gt;

&lt;p&gt;The most common Web Service APIs include SOAP, REST, and RPC. RESTful API is an API that conforms to the REST architecture style. It uses HTTP request methods to access resources and uses URIs (Uniform Resource Identifiers) to identify resources.&lt;/p&gt;

&lt;p&gt;The Hypertext Transfer Protocol (HTTP) is an application-layer protocol mostly used in RESTful APIs for communication between web browsers and servers. Each HTTP request message contains the host information, the HTTP protocol version (HTTP/1.1, HTTP/2), the HTTP methods (GET/POST), the HTTP headers (content type, content length), the actual message that is being transferred to the server, and the body, which contains that message.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Requests
&lt;/h3&gt;

&lt;p&gt;A correctly composed HTTP request contains three elements: the request line, header and message body.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request line: It consists of the request method, the request URI (which specifies the URI of the resource that the client is requesting, including the path, query parameters, etc.), and the HTTP protocol version number.&lt;/li&gt;
&lt;li&gt;Header: It contains information that a server can use to decide how to respond to the request.&lt;/li&gt;
&lt;li&gt;Message body: It contains the specific content of the request, such as form data, JSON data, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the following is an example of an HTTP request message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/v1/login HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 44

{"username": "user123", "password": "password123"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  URL
&lt;/h4&gt;

&lt;p&gt;A URL(Uniform Resource Locator) is an address used to locate resources on the Internet. It usually consists of a protocol type, hostname, port number, path, and query string. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.example.com:8080/api/login?param1=value1&amp;amp;param2=value2"&gt;https://www.example.com:8080/api/login?param1=value1&amp;amp;param2=value2&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In which：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https&lt;/code&gt; is the protocol type&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;www.example.com&lt;/code&gt; is the server address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;8080&lt;/code&gt; is the port number that the server is listening on&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/login&lt;/code&gt; is the path to the resource being accessed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;param1=value1&amp;amp;param2=value2&lt;/code&gt; is the query string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By requesting a URL, the client can send a request to the server and obtain the corresponding messages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Request Method
&lt;/h4&gt;

&lt;p&gt;The standard RESTful API only has four methods: GET, POST, PUT, and DELETE.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s3fGxbit--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2AJ0fIyee3ABWD1ju1olbsCw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s3fGxbit--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2AJ0fIyee3ABWD1ju1olbsCw.png" alt="img" width="800" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Request Headers
&lt;/h4&gt;

&lt;p&gt;An HTTP request header is &lt;strong&gt;a component of a network packet sent by client to the server to request for a specific page or data on the Web server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the example above, the Request Header includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host: The domain name of the server (for virtual hosting), and the TCP port number on which the server is listening.&lt;/li&gt;
&lt;li&gt;User-Agent: The &lt;a href="https://en.wikipedia.org/wiki/User_agent_string"&gt;user agent string&lt;/a&gt; of the user agent, i.e. Firefox 58.0 browser on Windows 10 operating system.&lt;/li&gt;
&lt;li&gt;Accept: Media type(s) that is/are acceptable for the response.&lt;/li&gt;
&lt;li&gt;Content-Type: Type of data the browser will accept in return&lt;/li&gt;
&lt;li&gt;Content-Length: The length of the request body in octets&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Message body
&lt;/h4&gt;

&lt;p&gt;A message body is &lt;strong&gt;data sent by the client to your API,&lt;/strong&gt; mostly used in POST, PUT and PATCH methods.&lt;/p&gt;

&lt;p&gt;The format and content of the message body are usually specified by the Content-Type field in the request header&lt;/p&gt;

&lt;p&gt;These are three common types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Raw&lt;/strong&gt;. This can technically hold any kind of string, but generally includes XML or JSON, e.g. &lt;code&gt;{"firstName":"Pied","lastName":"Piper"}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;x-www-form-urlencoded&lt;/strong&gt;. This is used for sending simple text values in a query string, e.g. &lt;code&gt;key1=value1&amp;amp;key2=value2&lt;/code&gt;. All characters are url encoded, e.g. spaces are replaced by &lt;code&gt;%20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary&lt;/strong&gt;. This is used for attaching images, videos, audio, and other non-text files.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  HTTP Responses
&lt;/h3&gt;

&lt;p&gt;HTTP responses, the answer from the server, consist of three parts: status line, header, and response body.&lt;/p&gt;

&lt;h4&gt;
  
  
  Status line
&lt;/h4&gt;

&lt;p&gt;The start line of an HTTP response, called the &lt;em&gt;status line&lt;/em&gt;, contains protocol version, status code and a status text for one to better understand the responses.&lt;/p&gt;

&lt;p&gt;eg: &lt;code&gt;HTTP/1.1 404 Not Found&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Headers
&lt;/h4&gt;

&lt;p&gt;The response header allow the server to pass additional information about the response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Message body
&lt;/h4&gt;

&lt;p&gt;The message body of a response contains the requested information requested by the client in the format specified by the Accept field in the request header.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of API Testing
&lt;/h3&gt;

&lt;p&gt;The aim of API testing is to test every interface in the system to verify that the interface meets the requirements and specifications in terms of functionality, performance, security, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Functional Testing
&lt;/h4&gt;

&lt;p&gt;Checks if the API works as inteded.&lt;/p&gt;

&lt;p&gt;API functional testing mainly focuses on the functionality of an API. It primarily evaluates specific functions inside the codebase. API testing guarantees that the API returns the intended result for a given input and handles issues when the output is outside the acceptable parameters. Negative or positive tests are the sub-types of functional tests, where negative tests examine how an API responds to each sort of incorrect input.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Performance Testing
&lt;/h4&gt;

&lt;p&gt;Verify the performance of the interface under high concurrency, large data volume, etc., including response time, throughput, number of concurrency, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stress testing&lt;/strong&gt;: Simulate multi-user concurrent access to the interface and observe the performance of the interface, such as response time, throughput, error rate, etc. Some tools can be used for stress testing, such as JMeter, LoadRunner, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load testing&lt;/strong&gt;: Test the performance of the interface under different loads. Load testing can be divided into two ways: static load and dynamic load. Static load refers to testing the performance of the interface under a predetermined number of concurrency, while dynamic load is to dynamically adjust the number of concurrency according to the actual load to test the performance of the interface under different loads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike testing:&lt;/strong&gt; Determine how an API responds to sudden, unexpected spikes in traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soak testing:&lt;/strong&gt; Soak testing can be achieved by running for a long period of time to observe the stability and reliability of the interface over an extended period of time&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Security Testing
&lt;/h4&gt;

&lt;p&gt;Testing the security of the API, including its protection capability, authentication and authorization, data encryption, etc.&lt;/p&gt;

&lt;p&gt;For security testing, different testing methods and techniques need to be used for different security risks and threats. It is also necessary to consider the security of the testing environment, such as the protection of test data and security management during the testing process. In order to improve testing efficiency and coverage, security testing tools can be used to assist testing, such as vulnerability scanning tools, code static analysis tools, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Do API Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reviewing the specifications of the API:&lt;/strong&gt; Reviewing the specifications of the API implies studying its functioning, exploring its objectives, and what one can expect from it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determining the requirements of API testing:&lt;/strong&gt; you must identify the API’s testing needs as this will necessitate an understanding of the API’s target consumer, its features and functionalities, the application’s workflow, as well as the aspects, priorities, and problems you’re testing for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defining the input parameters:&lt;/strong&gt; You must define input parameters before executing an API. These parameters provide important information to the API for it to fulfill its job and are thus required for establishing if the API functions as planned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing test cases for API testing:&lt;/strong&gt; Positive tests are intended to verify the API’s fundamental functionality using mandatory parameters as well as additional capabilities utilizing optional options. Negative tests are used to see how the API reacts to disallowed actions with valid and invalid user input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selecting an API testing tool:&lt;/strong&gt; Postman can be a great tool for monitoring API, creating automatic tests, identifying and removing bugs and running specific requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Send request to verify response
&lt;/h3&gt;

&lt;p&gt;AREX is an open-source testing tool(&lt;a href="https://github.com/arextest"&gt;https://github.com/arextest&lt;/a&gt;), a great Postman alternative for API testing. Here is a quick demonstration of testing an API with AREX.&lt;/p&gt;

&lt;p&gt;First create a new request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lAx2xF1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ARQ3E4h8WBZNHr89LXaXA0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lAx2xF1M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ARQ3E4h8WBZNHr89LXaXA0w.png" alt="img" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Add request URL
&lt;/h4&gt;

&lt;p&gt;Enter the URL of the API you want to send the request to in the address field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ko82c8VN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ASJG6uncLEHSXRsLPWs4y-Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ko82c8VN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ASJG6uncLEHSXRsLPWs4y-Q.png" alt="img" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Select request method
&lt;/h4&gt;

&lt;p&gt;Once the request is created, the request method is selected as GET by default, which means retrieve data from an API.&lt;/p&gt;

&lt;p&gt;Or you can select other method from the dropdown.&lt;/p&gt;

&lt;h4&gt;
  
  
  Query parameters
&lt;/h4&gt;

&lt;p&gt;A query string is a string of characters added to the end of a URL after &lt;code&gt;?&lt;/code&gt; and separated by &lt;code&gt;&amp;amp;&lt;/code&gt; to pass additional information to the API.&lt;/p&gt;

&lt;p&gt;A typical example like &lt;code&gt;https://www.example.com/search?q=apple&amp;amp;category=fruits&lt;/code&gt;, with two pairs of &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the query string parameter name in the &lt;code&gt;KEY&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;Set the query string parameter value in the &lt;code&gt;VALUE&lt;/code&gt; column.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h45kMVzW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2AUoC-kEqT-Ic8xCwUVI6xhQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h45kMVzW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2AUoC-kEqT-Ic8xCwUVI6xhQ.png" alt="img" width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Request headers
&lt;/h4&gt;

&lt;p&gt;If you need to send specific request header information with your request, you can add request header by key-value pair.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8NTk2via--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ALLkb4965w1rvqS3XC2zqhA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8NTk2via--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ALLkb4965w1rvqS3XC2zqhA.png" alt="img" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Request Body
&lt;/h4&gt;

&lt;p&gt;If you want to send data from the client to an API, you need to send the request body data with the request, commonly used in PUT, POST, and PATCH requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AWiMDbcl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2Aql1AAs9O9phd39aetaXESw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AWiMDbcl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2Aql1AAs9O9phd39aetaXESw.png" alt="img" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Writing Scripts
&lt;/h4&gt;

&lt;p&gt;There are two types of scripts, &lt;strong&gt;Pre-request Script&lt;/strong&gt; and &lt;strong&gt;Tests&lt;/strong&gt;, which correspond to the two phases of API &lt;strong&gt;before request&lt;/strong&gt; and &lt;strong&gt;after return response&lt;/strong&gt; respectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pre-request Script&lt;/strong&gt; are JavaScript codes executed before the API request, which can be used to add authentication information, set request timeout, check the format of request parameters, etc. AREX provides commonly used predecessor scripts, which can be used directly by clicking on them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dOwIC4bA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ASrwHydFH_IxbpmtwCHJQcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dOwIC4bA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2ASrwHydFH_IxbpmtwCHJQcw.png" alt="img" width="800" height="668"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tests are JavaScript codes that are executed after the API request returns data. It is mainly used to test (assert) the correctness of the results returned by the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cw0gvgwM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2A-9N8Z5o755EopvuBbgT0Hw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cw0gvgwM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2A-9N8Z5o755EopvuBbgT0Hw.png" alt="img" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have configured the request parameters, click Send to get the response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Return Response
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q7LR7-ln--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2AFmZaW_GxbnJ6sYM-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q7LR7-ln--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2AFmZaW_GxbnJ6sYM-.png" alt="img" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The response code, response time(the time in milliseconds it took for the response to arrive from the server) and response size can be seen at the top of the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response Body&lt;/strong&gt; is the body of the response, i.e. the content of the response returned from the server, and the data format of the content is JSON by default.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Raw&lt;/strong&gt; view allows you to see the raw response body content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u9VgAkV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2Ao-EOpwW9TAT9p07y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u9VgAkV---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2Ao-EOpwW9TAT9p07y.png" alt="img" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headers&lt;/strong&gt; shows the response header information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--btHOBcdf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2ALynfFoMjm9X9xElf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--btHOBcdf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/0%2ALynfFoMjm9X9xElf.png" alt="img" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a &lt;strong&gt;Test&lt;/strong&gt; script is set, you can view the results of executing the script in &lt;strong&gt;Result&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o-yx3qcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2A-cx5I5OKkd7XwFrPnJR2BQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o-yx3qcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1320/1%2A-cx5I5OKkd7XwFrPnJR2BQ.png" alt="img" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Community⤵️&lt;br&gt;
🐦 &lt;a href="https://twitter.com/AREX_Test"&gt;Follow us on Twitter&lt;/a&gt;&lt;br&gt;
📝 &lt;a href="https://arexcommunity.slack.com/ssb/redirect"&gt;Join AREX Slack&lt;/a&gt;&lt;br&gt;
📧 &lt;a href="https://groups.google.com/g/arex-test"&gt;Join the Mailing List&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>testing</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Test your application by using real traffic from production</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 14 Sep 2023 02:42:44 +0000</pubDate>
      <link>https://dev.to/arex_test/best-practice-of-developer-testing-with-arex-132m</link>
      <guid>https://dev.to/arex_test/best-practice-of-developer-testing-with-arex-132m</guid>
      <description>&lt;p&gt;Regression testing is a type of software testing that investigates the validity of previously-tested software. It ensures the software still functions as expected after it has been modified and integrated with other software, tools, and interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/arextest"&gt;AREX&lt;/a&gt; is an open-source automated regression testing platform, which allows one to record production traffic and replay it to testing environment. By comparing the recorded responses against replay, it can clarify the impact of code changes to realize efficient automated regression testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Save 99% man-cost on regression&lt;/strong&gt;: Our innovative record and replay approach eliminates the effort to write or maintain an automation suite. Testing without writing tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0 database contamination&lt;/strong&gt;: Mock external sub-calls in the requests to isolate the third-database called during replay, without causing any dirty data to the database or cache of the system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-speed regression testing across environments&lt;/strong&gt;: Automatically reproduces the production environment by mocking all the external dependencies. With just a single click to start, and the use of multi-threading and random execution, it achieves high-speed replay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For QA engineers, they don't need to write or maintain regression suite by using AREX to record online traffic as test cases. For developers, it enables them to get quick feedback to debug failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Developer Testing (DevTest)
&lt;/h2&gt;

&lt;p&gt;Developer testing is the act of regression testing source code by developers after the development of new features - instead of relying entirely on a team of QA experts to carry out testing.&lt;br&gt;
Here are some significant benefits with regard to developer testing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Higher code quality because new code is tested early and often.&lt;/li&gt;
&lt;li&gt;Shortened time to market for new features.&lt;/li&gt;
&lt;li&gt;The cost of change rises exponentially the longer it takes to find and then remove a defect.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  How to do DevTest with AREX
&lt;/h2&gt;

&lt;p&gt;Developers can use the standalone mode of AREX for quick self-testing of code changes before the test is submitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/arextest/arex-standalone"&gt;AREX Standalone mode&lt;/a&gt; refers to the independent operation of AREX in a local environment without the need to build storage services or other service components.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install AREX on a Mac
&lt;/h3&gt;

&lt;p&gt;Install AREX Standalone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AREX_AGENT_MAJOR_VERSION=1.0.1 bash -c "$(curl https://raw.githubusercontent.com/arextest/deployments/dev/shell/arex.standlone.install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As follows, the latest version of the executable file has been successfully downloaded and installed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslimww6hml82ywmgy6g7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslimww6hml82ywmgy6g7.png" alt="installation" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Install on Windows
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the &lt;a href="https://github.com/arextest/arex-standalone/releases"&gt;&lt;code&gt;arex-standalone-all.zip&lt;/code&gt;&lt;/a&gt; from "Assets" on the release page and unzip it.&lt;/li&gt;
&lt;li&gt;Run the &lt;code&gt;arex-cli.bat&lt;/code&gt; script under &lt;code&gt;arex-standalone-all&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Record
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Before developing new features, start the stable version of the application, and run &lt;code&gt;./arex-cli.sh&lt;/code&gt; to attach to the application which you want to test.&lt;/li&gt;
&lt;li&gt;For the first time to use, you need manually perform some actions to the application, so that AREX can record these requests as test cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nzv2h89xhk8cmx49560.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nzv2h89xhk8cmx49560.png" alt="ls" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;ls -o 3&lt;/code&gt; to view the recorded requests of /biz/getOrderInfo interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lwj233xzzp9skejr8vo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3lwj233xzzp9skejr8vo.png" alt="recorded interfaces" width="748" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All requests are listed in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cca0m67pnt8evviu5m7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4cca0m67pnt8evviu5m7.png" alt="recorded cases" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Detail&lt;/code&gt; to view the details:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fak1vaswnvfcvjjwbeohk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fak1vaswnvfcvjjwbeohk.png" alt="recorded details" width="800" height="899"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Replay
&lt;/h3&gt;

&lt;p&gt;When a new feature or change is expected to be tested, start the latest version of the application with new feature or changes, and run &lt;code&gt;./arex-cli.sh&lt;/code&gt; to attach Agent to the application.&lt;/p&gt;

&lt;p&gt;Replay the recorded cases in the testing environment. After replay, AREX will compare the recorded responses against replay.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8v8vfk3sgkevqt41zwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc8v8vfk3sgkevqt41zwv.png" alt="replay" width="793" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run watch to check the differences between the two requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gg6trcu39qlui4t04ck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gg6trcu39qlui4t04ck.png" alt="diff" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This help devs to identifies issues early, which allows them quickly test changes or new feature.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
