<?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: Jing</title>
    <description>The latest articles on DEV Community by Jing (@lijing-22).</description>
    <link>https://dev.to/lijing-22</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%2Fuser%2Fprofile_image%2F1112563%2F82a8c4b6-eef7-4c77-8880-39c01b79b23e.jpg</url>
      <title>DEV Community: Jing</title>
      <link>https://dev.to/lijing-22</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lijing-22"/>
    <language>en</language>
    <item>
      <title>Automated API testing made easy with AREX</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Mon, 24 Jun 2024 08:46:32 +0000</pubDate>
      <link>https://dev.to/lijing-22/automated-api-testing-made-easy-with-arex-26c1</link>
      <guid>https://dev.to/lijing-22/automated-api-testing-made-easy-with-arex-26c1</guid>
      <description>&lt;p&gt;Traditional automation testing often requires significant human resources for test data preparation and script creation, and may not provide adequate coverage. To maintain the stability of online systems, both developers and testers face the following challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After development, it can be challenging to quickly verify locally and identify initial issues.&lt;/li&gt;
&lt;li&gt;Preparing test data, writing and maintaining automation scripts are time-consuming and may not provide adequate coverage.&lt;/li&gt;
&lt;li&gt;It is difficult to verify the WRITE calls, and testing may produce dirty data, such as in our core trading system, which may write data to databases, message queues, Redis, etc. This data is often difficult to verify, and the data generated by testing is also difficult to clean up.&lt;/li&gt;
&lt;li&gt;It's difficult to reproduce production issues locally for testing and debugging.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;AREX solves the challenges of automated testing by replicating real online traffic to the test environment for automated API testing.&lt;/p&gt;

&lt;p&gt;AREX captures request parameters, return results, and some snapshot data during execution, such as database access parameters and results, as well as parameters and results for accessing remote servers, using AOP. It then sends the snapshot data to the test machine (the machine where code changes occur) to complete a replay process. By comparing the stored data, the data from calling backend requests, and the return results with the data from actual online requests, differences are identified to detect issues within the tested system.&lt;/p&gt;

&lt;p&gt;AREX can record all operations of the application's underlying dependencies on external systems, including database operations and requests to external systems. During replay testing, when related methods are triggered, AREX extracts information from the recorded data and returns it directly to the application, avoiding interactions with the actual database or other dependencies, reducing reliance on specific environmental data, and focusing on validating the program's logic and functionality.&lt;/p&gt;

&lt;p&gt;AREX also supports testing of write interfaces perfectly. For example, in critical scenarios such as order storage and calling third-party payment interfaces, the core mechanism of AREX traffic replay is to intercept and mock framework calls, using recorded data to replace actual data requests, ensuring that no real external interactions, such as database write operations or third-party service calls, occur during the testing process, effectively preventing the writing of dirty data during replay testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&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%2Fmw6mz3e0inld0ivxofpa.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%2Fmw6mz3e0inld0ivxofpa.png" alt="Workflow" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Traffic Capture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AREX Java Agent, attached to Java applications in the production environment, records both inbound traffic to your API and outbound traffic to its dependencies, including the resulting responses.&lt;/li&gt;
&lt;li&gt;Recorded data is forwarded to AREX Storage Service for storage in a MongoDB database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Traffic Replay:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Schedule Service replays the recorded application traffic back to the same application in the test environment, simulating production environment behavior.&lt;/li&gt;
&lt;li&gt;The application in the test environment is also attached with the AREX Java Agent. When the application try to call to any dependencies like DBs, other services, AREX Agent will intercept and provide the previously recorded dependency response. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Result Verification and Reporting:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AREX compares API responses and outbound traffic to dependencies, like databases or external services, with previously recorded traffic, and then generates a report.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Core Advantages
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;High Coverage Without Writing Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No code intrusion, minimal integration cost&lt;/li&gt;
&lt;li&gt;No need to write test cases, a massive amount of online requests ensures high coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Automate Testing with Mocks, No Need to Setup Test-environment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;During replay, application avoids actual calls to the database and other downstream components by mocking dependencies. It utilizes previously captured requests and responses, eliminating the need to maintain active dependencies for testing.&lt;/li&gt;
&lt;li&gt;Supports test &lt;strong&gt;WRITE calls&lt;/strong&gt;, including validation of database, message queue, Redis data, and even runtime memory data without generating dirty data during testing. Replace external dependencies with mock data.&lt;/li&gt;
&lt;li&gt;Supports automatic data collection and mocking for various mainstream technology frameworks, and supports local time, caching, and accurately reproduces the production data environment during replay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Secure and Stable&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In terms of data security, it offers comprehensive permission control and traffic desensitization mechanisms. &lt;/li&gt;
&lt;li&gt;Code isolation is implemented along with health management, and during system busy times, it intelligently reduces or stops data collection frequency, not affecting online applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Lower test noise&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strategies to manage noise involve executing tests repeatedly to detect inconsistent fields and employing methods such as time mocking to prevent session token expiration.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Community⤵️&lt;/p&gt;

&lt;p&gt;⭐ Star us on &lt;a href="https://github.com/arextest/arex-agent-java"&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>programming</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Best Practice for Production Traffic Replication in Trip.com</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Mon, 06 May 2024 03:02:40 +0000</pubDate>
      <link>https://dev.to/lijing-22/best-practice-for-production-traffic-replication-in-tripcom-1p3k</link>
      <guid>https://dev.to/lijing-22/best-practice-for-production-traffic-replication-in-tripcom-1p3k</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;AREX is an open-source API testing platform developed by &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt; using the concept of production traffic replication. It focuses on the construction of core linkages for recording and playback, evolving from basic solution architecture to deep implementation verification across the corporation’s core business lines. Through continuous iterations and optimizations amidst the group’s complex business scenarios, AREX has accumulated a vast amount of experience and achieved tangible results. Since its adoption by &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt;, over 4000 applications have been integrated with AREX, leading to improved delivery rates and a reduction in defects.&lt;/p&gt;

&lt;p&gt;This article primarily discusses the range of challenges encountered and the solutions devised during the implementation of AREX within &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt;, as well as how to utilize AREX to rapidly deploy a one-stop traffic recording and playback solution to reduce integration costs and accelerate implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is production traffic replication and replay?
&lt;/h3&gt;

&lt;p&gt;Production traffic replication and replay is &lt;strong&gt;a technique used to capture network traffic in one environment — typically a production environment — and then replay that traffic in another environment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It has a promising future for the performance, regression and automated testing. It provides a crucial solution for the technology team to cope with complex business and system architectures while ensuring system stability and improving efficiency in the R&amp;amp;D process.&lt;/p&gt;

&lt;p&gt;However, deploying technological solutions is not without challenges. Teams often encounter difficulties in infrastructure development, disproportionate initial investment costs relative to early returns, and ambiguities surrounding practical application scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://docs.arextest.com/blog/trip-arex#solution" rel="noopener noreferrer"&gt;Sharp tools make good work&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The most common open-source solution for “Production traffic replication” is based on Jvm-Sandbox-Repeater secondary development and transformation. The core principle is to record the real traffic on the line and then playback in the test environment to verify the correctness of the code logic. You may ask: Since there are mature solutions, why are we still “reinventing the wheel”?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Firstly&lt;/strong&gt;, the components supported by JVM Sandbox are limited and cannot meet the needs of the middleware and frameworks widely used within &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt;. Additionally, the underlying support of the JDK is not thorough enough, such as the asynchronous thread context transfer, which requires reliance on other third-party components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moreover&lt;/strong&gt;, while Jvm-Sandbox-Repeater provides basic recording and playback functionality, to build a comprehensive regression testing platform, we also need a robust backend support system responsible for data collection, storage, and comparison tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lastly&lt;/strong&gt;, the lack of official documentation and community activity put us at risk of not being able to get official support in time for subsequent secondary development.&lt;/p&gt;

&lt;p&gt;So we decided to independently develop the traffic recording and replay tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Support for more middleware and components recording and replay, and the capability to simulate a variety of complex business cases, such as local caching, the time, and so on.&lt;/li&gt;
&lt;li&gt;As a comprehensive solution, it also comes equipped with a complete set of supporting facilities, including a frontend interface, playback service, and report analysis, to achieve an all-in-one workflow from traffic collection and playback to comparison verification and report generation (as shown in the figure below).&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2A99NIYnHWJf8UYrJb.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2A99NIYnHWJf8UYrJb.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we will delve into the challenges encountered during the implementation process, targeted solutions, and internal application examples within our group, in the hope of providing you with substantial help and guidance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges Faced in Development
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How to capture traffic in cross-thread and asynchronous cases&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;First of all, it needs to be made clear that the captured traffic we are referring to here is all the data involved in sending an API request, including not only the main endpoint but also the internal requests and responses of various frameworks such as Mybatis, Redis, Dubbo, and more.&lt;/p&gt;

&lt;p&gt;However, many projects in our company utilize thread pools and asynchronous programming scenarios. For example, in a single request, the main process may fork multiple subtasks/threads to work in parallel. Some tasks may query Redis, some may call RPC interfaces, and others may perform database operations to fulfill different business scenarios. This also involves significant thread switching at the underlying level.&lt;/p&gt;

&lt;p&gt;To ensure that operations executed in different threads within a single request are captured, we address this issue by using the approach of Trace propagation. This involves decorating various thread pools and asynchronous frameworks, using a recordId to pass between threads, thereby linking them together to complete a complete recording of a test case. For example, in Java, this can be achieved by decorating CompletableFuture, ThreadPoolExecutor, ForkJoinPool, as well as third-party thread pools used by Tomcat, Jetty, Netty, and asynchronous frameworks like Reactor and RXJava. This enables the propagation of the recordId across different threads.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;How to avoid writing dirty data to the database during replay?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;For example, in critical scenarios such as generating orders to the database and calling of third-party payment interfaces, it is necessary to use “mock” data during traffic replay to avoid actual data interactions. This approach prevents the generation of unnecessary data during testing and avoids disrupting normal business processes.&lt;/p&gt;

&lt;p&gt;To achieve this, framework calls need to be intercepted and mocked, using recorded data instead of real data requests. This ensures that no actual external interactions, such as database writes or third-party service calls, occur during the testing process, effectively preventing the writing of dirty data during replay testing.&lt;/p&gt;

&lt;p&gt;AREX Java Agent has supported most open-source frameworks such as Spring, Dubbo, Redis, Mybatis, and more. Please refer to the list below for the complete list:&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AM_T7zq4nbPcuLGIf.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AM_T7zq4nbPcuLGIf.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Replay response differs from the recorded one, but not caused by a code bug&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Login authentication and token expiration&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During the actual process of traffic replay, we often encounter an issue: many web applications implement login authentication checks before accessing their interfaces. If the authentication fails or the login token has expired, the interface access will be denied, resulting in a large number of test cases failing during replay, which means replay response differs from the recorded one. While it is possible to address some of these issues by configuring a whitelist, we are seeking a more general solution.&lt;/p&gt;

&lt;p&gt;The ideal solution would be to mock authentication frameworks such as Spring Security, Apache Shiro, JWT, etc., during the replay process. This would bypass the authentication and token verification steps, ensuring that the interfaces can be executed smoothly in the replay environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Time inconsistencies cause payment timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the current time during recording and replay are not consistent, it may result in unexpected differences in timeout logic. For example, in cases where we determine if an order has timed out and not been paid, we often use the condition “currentTime — orderCreateTime &amp;gt; 30 minutes” as the basis for judgment. If the order has not timed out during recording but during replay, half an hour later, due to the change in the system’s current time, it may mistakenly trigger the payment timeout processing logic.&lt;/p&gt;

&lt;p&gt;To address this issue, we have proposed a solution: during the recording process, record the current time at that moment and only record it once. During the replay process, we use Mock to simulate classes related to the current time, such as Date, Calendar, LocalTime, joda.time, etc., so that the current time used during replay is actually the time recorded during the recording process. This ensures that time-related logic during replay is consistent with the recording, ensuring the accuracy and reliability of the test results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Local cache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In applications, it is common practice to optimize performance by storing frequently accessed data in local caches for faster retrieval. However, in traffic recording and replay scenarios, the behavior of the cache can introduce variations in the replay results.&lt;/p&gt;

&lt;p&gt;During recording, if the requested data is already cached, the system retrieves it directly from the cache, avoiding queries to the database or external interfaces. However, in the replay environment, without preloaded cached data, the same request may trigger database queries or external interface calls, resulting in new calls and causing replay failures.&lt;/p&gt;

&lt;p&gt;To address this, we have supported for popular caching frameworks such as &lt;strong&gt;Guava Cache&lt;/strong&gt; and &lt;strong&gt;Caffeine Cache&lt;/strong&gt;, ensuring the requests to the cache during replay can return the expected results based on the recording state, avoiding unnecessary new calls.&lt;/p&gt;

&lt;p&gt;For cases where custom caching frameworks are used, the AREX platform provides flexible configuration options that allow adaptation through dynamic classes. This means that even non-standard caching implementations can be compatible with the AREX platform and correctly support traffic replay.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://docs.arextest.com/blog/trip-arex#implementation-challenges" rel="noopener noreferrer"&gt;&lt;/a&gt;Challenges Faced in Implementation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Installation and deployment should be simple, convenient, and easy to get started with&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AREX is a comprehensive solution that includes not only the core recording and replay functionality but also complementary services such as frontend, scheduling, report analysis, and storage. Following the principle of out-of-the-box usability and quick integration, we offer multiple deployment options, including one-click deployment, non-container deployment, and private cloud deployment. Once installed, you only need to configure some basic parameters to automatically capture traffic and replay it to compare differences.&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AXZb2nv1q9dpbyvMM.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AXZb2nv1q9dpbyvMM.png" alt="img"&gt;&lt;/a&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2ACcsPMOU9PI54XvPW.jpg" 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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2ACcsPMOU9PI54XvPW.jpg" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, AREX also support a standalone mode, which allows you to quickly get started and experience the platform without the need for installation on your local machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Complying with the company’s risk control and data security requirements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When recording real production traffic, it is necessary to apply data masking rules to sensitive information to ensure reliable protection of sensitive and private data, especially in cases involving information security or sensitive commercial data. This involves transforming sensitive data into a masked or altered form to prevent unauthorized access and protect the privacy of sensitive information.&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2ALvAjpOXSMy2Xg2Q7.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2ALvAjpOXSMy2Xg2Q7.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have chosen to perform data masking during the data storage process to ensure the security of sensitive information. The specific implementation involves using the SPI (Service Provider Interface) mechanism to load external JAR packages and dynamically load encryption methods.&lt;/p&gt;

&lt;p&gt;By utilizing the SPI mechanism, we can extend the functionality of the data storage process by loading custom encryption modules from external JAR packages. These modules can then be dynamically loaded and applied to the sensitive data before it is stored in the database.&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F1%2AbNEUqVyJ3UMAnC6uXpVWDQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F1%2AbNEUqVyJ3UMAnC6uXpVWDQ.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Improve user experience and identify issues quickly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In actual usage, there is a huge number of test cases for recording and replaying. To reduce the workload of users when analyzing differences, we have implemented aggregation for test cases with the same differences. This speeds up the process of troubleshooting and issue identification.&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2Ar99drzDObzE33cuj.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2Ar99drzDObzE33cuj.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With call chains, it is possible to quickly identify the scope of the problem and reduce interference by removing noise nodes such as timestamps, UUIDs, and IP addresses. This helps to minimize distractions and focus on the relevant information for issue identification.&lt;/p&gt;

&lt;p&gt;If it is difficult to reproduce online issues locally in complex business applications, AREX also supports local debugging to quickly troubleshoot problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Is AREX mature, secure, and reliable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AREX is based on Java Agent technology and utilizes the mature bytecode modification framework, ByteBuddy. It is secure, stable, and features code isolation and self-protection mechanisms. It intelligently reduces or disables data collection frequency during system busy periods. Moreover, it has been running steadily within Ctrip Group for over two years and has been thoroughly validated in production environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practice&lt;a href="https://docs.arextest.com/blog/trip-arex#best-practice" rel="noopener noreferrer"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Currently, the AREX traffic recording and replay platform has been integrated as a standalone option into the company’s CI/CD system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The initial onboarding process for AREX&lt;/strong&gt;: When users onboard the traffic recording and replay feature for the first time, they simply need to select the Flight AREX Agent service in the CI Pipeline. This ensures that during the application packaging process into an image, the AREX startup script, &lt;code&gt;arex-agent.sh&lt;/code&gt;, will be included in the release package.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment and Agent loading&lt;/strong&gt;: During the application deployment process, the previous script will first pull the latest arex-agent.jar and mount the AREX Agent by modifying the JVM options (-javaagent:/arex-agent.jar).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control and Canary Release&lt;/strong&gt;: After the startup script is executed, the corresponding &lt;code&gt;arex-agent.jar&lt;/code&gt; version will be pulled based on the AppId of the application to achieve canary release and on-demand loading. For example, only certain specific applications will load the new Agent features.&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2Ay8T1xSBASbUEFV3_.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2Ay8T1xSBASbUEFV3_.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, if it is the first replay, the operation is also simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Pipeline&lt;/strong&gt;: In GitLab or Jenkins, create a pipeline and in the ArexTest job script, call the playback URL provided by AREX and schedule the pipeline to run at regular intervals.&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AueJRiZDZTLNRoFrA.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F0%2AueJRiZDZTLNRoFrA.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Automatically triggered traffic replay&lt;/strong&gt;: Traffic replay is automatically triggered by developers after committing code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Push the replay report and control release&lt;/strong&gt;: After replay, AREX will push metrics such as the number of test cases, pass rate, failure rate, etc., to the relevant personnel for statistical analysis. Only when the pass rate meets the predetermined criteria, the code is allowed to be deployed to the production environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Landed results&lt;a href="https://docs.arextest.com/blog/trip-arex#landed-results" rel="noopener noreferrer"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Under the continuous iteration and optimization in complex business scenarios within the group, the AREX platform has accumulated a wealth of experience and achieved visible results. Since its implementation in &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt;, it has been adopted by over 4000 applications, resulting in improved delivery rates and reduced defect counts.&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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F1%2ALrO2vSg_mCcDhG6JOd0VCQ.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%2Fcdn-images-1.medium.com%2Fmax%2F1080%2F1%2ALrO2vSg_mCcDhG6JOd0VCQ.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Embracing Open Source&lt;a href="https://docs.arextest.com/blog/trip-arex#embracing-open-source" rel="noopener noreferrer"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;After long-term stable operation and verification of its reliability within &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt;, we have decided to open-source the AREX platform in 2023, with the aim of helping more enterprises efficiently and cost-effectively implement traffic recording and replay technology solutions. You can find the open-source project at &lt;a href="https://github.com/arextest" rel="noopener noreferrer"&gt;AREX GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the past year, we have also been committed to building an open-source community. Currently, there are thousands of external users who have adopted and are using AREX, and we have received positive feedback from these users.&lt;/p&gt;

&lt;h3&gt;
  
  
  In the end&lt;a href="https://docs.arextest.com/blog/trip-arex#in-the-end" rel="noopener noreferrer"&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;AREX is dedicated to ensure quality, reduce costs, and improve efficiency while meeting the demands of rapid iteration. This vision has been validated through the practices of &lt;a href="http://Trip.com" rel="noopener noreferrer"&gt;Trip.com&lt;/a&gt; and numerous open-source users, bringing significant business value.&lt;/p&gt;

&lt;p&gt;Looking ahead, we will continue to rely on the active community to respond to and address user inquiries, continuously optimizing AREX. We sincerely invite every developer to join the community, try it out, and witness the growth and progress of AREX together.&lt;/p&gt;

&lt;p&gt;Community⤵️&lt;/p&gt;

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

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>testing</category>
    </item>
    <item>
      <title>Try AREX for Shadow Testing with Zero Test Case Writing</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Tue, 06 Feb 2024 08:05:50 +0000</pubDate>
      <link>https://dev.to/lijing-22/try-arex-for-shadow-testing-with-zero-test-case-writing-18l9</link>
      <guid>https://dev.to/lijing-22/try-arex-for-shadow-testing-with-zero-test-case-writing-18l9</guid>
      <description>&lt;p&gt;As business systems evolve and become increasingly complex, quality engineers are constantly on the run. Traditional functional testing or automated testing practices no longer provide sufficient confidence in quality assurance, shadowed by the pain points of quality activities. These can be summarized as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;High script maintenance cost: The absence of comprehensive interface contract documentation makes the implementation of interface automation challenging, leading to high costs in writing and maintaining test cases due to iterative changes.&lt;/li&gt;
&lt;li&gt;Tight project timelines: Insufficient testing regression.&lt;/li&gt;
&lt;li&gt;Inadequate personnel experience &amp;amp; difficulty in test data creation: Script parameters fail to simulate real-world scenarios, making it harder to reproduce bugs.&lt;/li&gt;
&lt;li&gt;Complex traffic: High cost in constructing requests, leading to incomplete coverage of test scenarios.&lt;/li&gt;
&lt;li&gt;Business complexity: Difficulty in organizing business scenarios, with unrealistic traffic distribution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Shadow Testing is a technique that aims to replicate the production environment (referred to as V-Current) in order to simulate real user traffic. This is achieved by creating a shadow version of the production environment. The purpose of this approach is to test new features in the V-Next environment. Once the testing phase is completed, the responses from both environments are compared to mitigate any potential risks before deploying the new feature to the production environment.&lt;br&gt;
Shadow Testing has gained significant popularity within the testing community, mainly due to its effectiveness in conducting regression testing for large-scale services. It offers extensive coverage while keeping maintenance costs to a minimum.&lt;/p&gt;

&lt;p&gt;Although similar in its objective, the AREX implementation of regression testing differs slightly from shadow 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%2F2rvxt9gbve3h7c6r33be.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%2F2rvxt9gbve3h7c6r33be.png" alt="Image description" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Capture production traffic
&lt;/h2&gt;

&lt;p&gt;A crucial part of shadow testing is testing with production data.&lt;/p&gt;

&lt;p&gt;Java Agent is a powerful tool based on the JVM (Java Virtual Machine) that allows for the modification and enhancement of Java application bytecode at runtime, without the need to alter the application's source code. This capability makes Java Agent an ideal choice for implementing features such as monitoring, performance analysis, and traffic recording. By leveraging the Java Instrumentation API, agents can transform class files loaded by the JVM, enabling a wide range of dynamic analysis and manipulation tasks.&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding Traffic Capturing with Java Agent
&lt;/h3&gt;

&lt;p&gt;Traffic capturing refers to the process of capturing the input and output data of an application during its execution. This includes network requests, responses, and potentially database queries, among other types of interactions. It is invaluable for debugging, performance optimization, security testing, and more, offering insights into the application's behavior under various conditions.&lt;/p&gt;
&lt;h4&gt;
  
  
  Bytecode Instrumentation
&lt;/h4&gt;

&lt;p&gt;AREX Java Agent implements traffic recording primarily through bytecode instrumentation. Libraries such as ASM or Javassist are used to manipulate specific classes and methods at the bytecode level. This process, known as instrumentation, allows the agent to insert additional functionality for recording traffic without altering the existing business logic.&lt;/p&gt;
&lt;h4&gt;
  
  
  Data Capture
&lt;/h4&gt;

&lt;p&gt;During instrumentation, Java Agent can inject extra code at the entry and exit points of methods to capture input parameters and return values. For network requests and responses, enhancing classes and methods related to networking libraries enables the capture of this data. This approach ensures that the traffic recording is comprehensive and includes all relevant data flows within the application.&lt;/p&gt;
&lt;h4&gt;
  
  
  Serialization and Storage
&lt;/h4&gt;

&lt;p&gt;Once captured, the data needs to be serialized and stored for later access and analysis. Java Agent can be configured to use various serialization formats, such as JSON or XML, and storage mechanisms, including local files or remote databases. This flexibility allows developers to tailor the traffic recording to their specific needs, facilitating easier debugging and performance tuning.&lt;/p&gt;
&lt;h2&gt;
  
  
  Running tests and saving outputs
&lt;/h2&gt;

&lt;p&gt;Once we have the input data, the next step is to build and run the test.&lt;/p&gt;

&lt;p&gt;By leveraging the Java Agent to capture input parameters, we can accurately simulate requests to the application under test. Typically, the application being tested is also equipped with an agent. This setup allows for a nuanced approach to traffic replay where the primary interface calls are executed genuinely, while the sub-calls to third-party dependencies are not.&lt;/p&gt;

&lt;p&gt;During replay, if a sub-call's input parameters match those recorded during the traffic recording phase, the recorded data is returned directly, effectively mocking the sub-call. This method of replaying traffic by using Java Agent to mock sub-calls based on identical input parameters ensures a high fidelity simulation of the production environment. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of This Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The primary benefit of this strategy is the ability to recreate the production environment's conditions in the test environment without making actual calls to third-party services. This not only helps in isolating the application under test but also significantly reduces the complexity and cost associated with setting up external dependencies for testing purposes. Moreover, it allows developers and testers to focus on the application's behavior in conditions that closely mirror those found in production, facilitating more accurate performance evaluations and debugging processes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Comparing The Differences
&lt;/h2&gt;

&lt;p&gt;Now that we have the test result and the result of the production version. We need to find the differences.&lt;/p&gt;

&lt;p&gt;AREX not only compares the parameters and responses of the main-interface requests but also compares the request marameters of external dependencies.&lt;/p&gt;
&lt;h2&gt;
  
  
  A practical Example: Processing a Product Purchase Request
&lt;/h2&gt;

&lt;p&gt;Consider a RESTful service with an endpoint &lt;code&gt;/purchaseProduct&lt;/code&gt; that handles product purchase requests. When this endpoint is hit with an HTTP request, it triggers a method &lt;code&gt;processPurchase&lt;/code&gt; in the service to handle the purchase logic, including database interactions to check and update product stock.&lt;/p&gt;

&lt;p&gt;It involves both an entry point call and sub-calls to a database, providing a comprehensive example of traffic recording and replay using the AREX Java Agent.&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="cm"&gt;/**
 * Processes a purchase request for a given product.
 * It involves an HTTP request as the entry point and a database operation as the sub-call.
 *
 * @param productId The ID of the product being purchased.
 * @param quantity The quantity of the product being bought.
 * @return A response indicating the success or failure of the purchase.
 */&lt;/span&gt;
&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/purchaseProduct"&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;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;processPurchase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&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="c1"&gt;// Entrance Invocation: HTTP request to purchase a product&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;// SubInvocation 1: Check product availability in the database&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;availableStock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStockForProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productId&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;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;availableStock&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&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="c1"&gt;// SubInvocation 2: Update product stock in the database&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="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateProductStock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;availableStock&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;quantity&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;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isUpdated&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="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="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Purchase successful."&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="o"&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="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="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Insufficient stock."&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;
  
  
  Step 1: Traffic Recording
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Recording the Request&lt;/strong&gt;: The Java Agent begins by recording the incoming HTTP request for a product purchase, capturing details such as the request method, URL, parameters (&lt;code&gt;productId&lt;/code&gt;, &lt;code&gt;quantity&lt;/code&gt;), and any headers or cookies if applicable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recording Database Operations&lt;/strong&gt;: The agent then records the database queries made by the &lt;code&gt;processPurchase&lt;/code&gt; method. This includes checking the product's available stock and attempting to update the stock if sufficient. Each database operation's request and result are captured.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Traffic Replay
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Replaying the Request&lt;/strong&gt;: During the replay phase, the HTTP request to &lt;code&gt;/purchaseProduct&lt;/code&gt; is made genuinely, reflecting a real-world interaction as would occur in production. This ensures that the test conditions closely mimic actual user behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mocking Database Operations&lt;/strong&gt;: For database interactions, the agent checks if the product ID and quantity match the recorded data. If they do, it mocks the database responses based on the recorded outcomes, thereby simulating the database's behavior for both checking and updating stock without real database access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;To sum up, there are a lot of benefits to using AREX:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero impact on the production environment.&lt;/li&gt;
&lt;li&gt;No need to generate and maintain test cases and test data.&lt;/li&gt;
&lt;li&gt;Test real-life scenarios with real-life data.&lt;/li&gt;
&lt;li&gt;Great test coverage from using production data.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>showdev</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <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>Regression Testing: A Complete Guide</title>
      <dc:creator>Jing</dc:creator>
      <pubDate>Thu, 07 Dec 2023 07:38:01 +0000</pubDate>
      <link>https://dev.to/lijing-22/regression-testing-a-complete-guide-3ag8</link>
      <guid>https://dev.to/lijing-22/regression-testing-a-complete-guide-3ag8</guid>
      <description>&lt;p&gt;In the fast-paced landscape of rapid software development, where upgrades and modifications are frequent, it is crucial to ensure the stability and quality of software products. Regression testing, as a critical component of the testing process, plays a vital role in achieving this goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is regression testing, and why do we need it?
&lt;/h2&gt;

&lt;p&gt;Regression testing is a type of software testing conducted to confirm that a recent change or upgrade in the application has not adversely affected the existing functionalities. It is performed to ensure that the modifications or fixes made in one code module do not impact other modules due to dependencies. A tester typically initiates regression testing after a developer incorporates a new functionality into the application or completes fixing a current error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of regression tests
&lt;/h3&gt;

&lt;p&gt;Suppose you are working on an e-commerce website that allows users to search for products and make purchases. You have recently made some changes to the search functionality and want to perform regression tests to ensure that the changes have not introduced any new issues.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search functionality: First test the search functionality to ensure that it still works as expected. This would include testing the search bar, filters, sorting options, and search results.&lt;/li&gt;
&lt;li&gt;Product pages: Then test the product pages to ensure they still display the correct information and images. This would include testing the product description, price, availability, and reviews.&lt;/li&gt;
&lt;li&gt;Shopping cart: Test the cart to ensure it works as expected. This would include testing the ability to add and remove items, update quantities, and proceed to checkout.&lt;/li&gt;
&lt;li&gt;Payment processing: Test the functionality to ensure it still works as expected. This would include testing the ability to enter payment information, select a payment method, and complete the purchase.&lt;/li&gt;
&lt;li&gt;User accounts: Test the user accounts functionality to ensure it works as expected. This would include testing the ability to create an account, log in, view order history, and update account information.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Record &amp;amp; Replay Approach in regression testing
&lt;/h2&gt;

&lt;p&gt;Today's software applications are incredibly complex, with numerous microservices, containers, and connections. As a result, it's challenging to manually script test cases for every possible scenario, making it difficult to ensure that applications are bug-free.&lt;/p&gt;

&lt;p&gt;Despite the best efforts of QA teams, maintaining application stability and quality can be challenging. Writing and maintaining tests that fail to cover every user flow in the product is a time-consuming task, and workflows outside of these tests can break, leading to the generation of new bugs regularly.&lt;/p&gt;

&lt;p&gt;Fortunately, the record and replay approach used by AREX offers a solution to this problem. AREX monitors every user interaction, automatically building integration tests without requiring teams to write a single line of code. This comprehensive approach ensures that every possible API regression, including logical, functional, and performance failures, is caught.&lt;/p&gt;

&lt;p&gt;Compared to manually scripting and generating traffic, traffic replay has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates more realistic tests&lt;/li&gt;
&lt;li&gt;Reduces the cognitive load required to create tests&lt;/li&gt;
&lt;li&gt;Identifies bugs that may not be discovered in a controlled environment&lt;/li&gt;
&lt;li&gt;Leverages varied data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Record Application Traffic
&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%2Fzyc4qii8tfphopwuec5t.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%2Fzyc4qii8tfphopwuec5t.png" alt="Image description" width="797" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In general, the invocation chain of a request includes the entry point and various synchronous or asynchronous dependent calls. The recording process in AREX involves linking the entry point and the dependent calls together using a Record-Id, to save as a complete test case. The AREX-Agent achieves this by performing bytecode enhancement on the code of the entry point and dependent calls. When the code is executed, the invocation process is intercepted, and the input parameters, return values, and exceptions of the calls are recorded and sent to the storage service.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Replay Traffic to test a new build
&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%2F83xfdxdqfseh4yq3h5km.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%2F83xfdxdqfseh4yq3h5km.png" alt="Image description" width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During the replay mode, AREX Schedule Service retrieves the recorded data of the real traffic requests in the production environment and then sends these requests to the tested application in test environment. At the same time, the Java Agent returns the recorded responses of external dependencies to the tested application. &lt;/p&gt;

&lt;p&gt;AREX then compares the recorded responses with the responses of the version under test to verify the correctness of the system logic. This eliminates the need for QA teams to manually think and write all assertions, streamlining the testing process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices for regression testing
&lt;/h2&gt;

&lt;p&gt;For the demo, we use the community-test application as an example to demonstrate the entire process of AREX automation regression testing, from configuring the AREX Agent to recording and playback, and issue identification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start the AREX Docker Project
&lt;/h3&gt;

&lt;p&gt;The AREX project simplifies the setup, deployment, and execution of various services, including AREX-UI, Schedule Service, API Service, Storage Service, and others, by utilizing the existing pre-defined Docker Compose configuration file. With just a single command.&lt;/p&gt;

&lt;p&gt;First, clone the &lt;code&gt;deployments&lt;/code&gt; repo on GitHub:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&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%2Fdqjjzwpw7t77lnp5jx3h.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%2Fdqjjzwpw7t77lnp5jx3h.png" alt="Image description" width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then start AREX by simply running the &lt;code&gt;docker compose up&lt;/code&gt; command in a terminal from the project root folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installation, check the status and ports of each service by running &lt;code&gt;docker-compose ps&lt;/code&gt; on the host running Docker.&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%2F0tjc6pllqu1fn1s5we4l.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%2F0tjc6pllqu1fn1s5we4l.png" alt="Image description" width="721" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to &lt;a href="http://10.5.153.1:8088/"&gt;http://10.5.153.1:8088/&lt;/a&gt; where the &lt;code&gt;arex-front&lt;/code&gt; is running. Enter your email and you will receive a verification code to log in:&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%2Fr2sq5tt56onijkyq7t7m.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%2Fr2sq5tt56onijkyq7t7m.png" alt="Image description" width="760" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure AREX Java Agent
&lt;/h3&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;. To use AREX Java Agent for recording traffic in a Java application, you essentially integrate the AREX Java Agent into your application. This agent will intercept and record the traffic, such as HTTP requests and responses. AOP is used here to weave the recording logic into your application without modifying the source code directly. It adds the recording aspect at runtime, enabling the AREX Java Agent to capture and log the traffic seamlessly as part of the application's operation.&lt;/p&gt;

&lt;p&gt;This approach is efficient because it minimizes code intrusion while ensuring comprehensive traffic recording.&lt;/p&gt;

&lt;p&gt;First, clone and compile the AREX Agent.&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 https://github.com/arextest/arex-agent-java.git cd arex-agent-java mvn clean package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compilation complete:&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%2Fs993f07n54o1yoqt4mdv.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%2Fs993f07n54o1yoqt4mdv.png" alt="Image description" width="574" height="803"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Create an application in AREX
&lt;/h4&gt;

&lt;p&gt;To set up the application, navigate to the AREX UI(&lt;a href="http://10.5.153.1:8088/"&gt;http://10.5.153.1:8088/&lt;/a&gt;) and select Replay on the left-hand navigation bar. Click "+" to &lt;strong&gt;C&lt;/strong&gt;&lt;strong&gt;reate Application.&lt;/strong&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%2Fdxiekv2qfwxbrars3h9m.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%2Fdxiekv2qfwxbrars3h9m.png" alt="Image description" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Copy the &lt;strong&gt;Agent Script&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;After creating an application, the startup parameters for the Agent of the application will be automatically generated as shown 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%2F322tddiow90cnr36df2d.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%2F322tddiow90cnr36df2d.png" alt="Image description" width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replace the values in the pointed brackets as appropriate. You will use these parameters in the next step for the app config.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Start Application with AREX Java Agent
&lt;/h4&gt;

&lt;p&gt;Start the &lt;code&gt;community-test&lt;/code&gt; application with the former AREX Java Agent script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java -javaagent:/usr/local/tomcat/arex-agent-0.3.0.jar      -Darex.service.name=f27172b0e331a42a      -Darex.storage.service.host=10.5.153.1:8093      -jar community-service.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;'-javaagent:/usr/local/tomcat/arex-agent-0.3.0.jar' is the JAR file of the AREX Agent that we have compiled. This JAR file will be loaded into the application as a Java agent.&lt;/li&gt;
&lt;li&gt;-Darex.storage.service.host=10.5.153.1:8093: This is the address of the AREX Storage service, specified as 10.5.153.1:8093.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, the application is running successfully:&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%2Fb13vqodqwwqvzdpcwlzv.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%2Fb13vqodqwwqvzdpcwlzv.png" alt="Image description" width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Record requests in a production environment
&lt;/h3&gt;

&lt;p&gt;Now that arex-agent is successfully up and running in the application, the user does not need to perform any special operations or interventions during the recording process and can serve the application externally or access it manually in the usual way.&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%2Fv0ol6a5o1psrlqup8hdf.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%2Fv0ol6a5o1psrlqup8hdf.png" alt="Image description" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Updates
&lt;/h3&gt;

&lt;p&gt;Now you've got your business requirements sorted and it's time to test a new version. Just follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull the latest code.&lt;/li&gt;
&lt;li&gt;Compile and package it up.&lt;/li&gt;
&lt;li&gt;Heads up: Don't mess with the AREX Agent's config, especially the app name. AREX needs it to keep track of your app and handle the test cases. So, make sure the app name stays the same as before during compile and test.&lt;/li&gt;
&lt;li&gt;Roll out the code to your test environment: Ship your compiled and packaged app to the testing grounds for some serious trial runs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Replay Traffic to perform regression test&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now we need to perform a regression test on the new version of the application. Start replay testing and fill in the test environment (here is &lt;a href="http://10.5.153.1:8080/):"&gt;http://10.5.153.1:8080/):&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%2Fsnp0kn8puqmdju631avd.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%2Fsnp0kn8puqmdju631avd.png" alt="Image description" width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If there are no discrepancies in the comparison between the recorded responses with the responses of the version under test, the test passes:&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%2Foujs8dkyelgrytp4q837.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%2Foujs8dkyelgrytp4q837.png" alt="Image description" width="800" height="46"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the test failed, that means there is a discrepancy in the two responses. Then we need to 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%2Fr43b38c9f7mw6emomgty.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%2Fr43b38c9f7mw6emomgty.png" alt="Image description" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The playback report will display all the interfaces and the test results, as shown 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%2F08wv3pzdrr22nn0mfgta.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%2F08wv3pzdrr22nn0mfgta.png" alt="Image description" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When compared, any deviations between the 2 versions is a possible breaking change in the version under test.&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%2F6t7lnfo7hkz9utjzjegc.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%2F6t7lnfo7hkz9utjzjegc.png" alt="Image description" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you spot discrepancies, here's how to iron them out in the code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify the issue and implement fixes.&lt;/li&gt;
&lt;li&gt;Cycle back through the 'Code Update' steps: modify, test, deploy, and compare again.&lt;/li&gt;
&lt;li&gt;If it turns out the discrepancy isn't an issue, label that section to bypass it in future playbacks.&lt;/li&gt;
&lt;li&gt;Keep iterating this process until all discrepancies are either resolved or identified as within acceptable limits.&lt;/li&gt;
&lt;li&gt;Once all fixes are confirmed, go ahead with releasing the updated version.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>programming</category>
      <category>devops</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>
  </channel>
</rss>
