# How to Utilize Java 'Records' in Test Automation

## Introduction

Writing clean, maintainable test automation code is crucial for ensuring long-term project success. One of the challenges test engineers face is managing the repetitive, boilerplate code needed for data models in test cases. Fortunately, **Java 14** introduced a [preview feature](https://openjdk.org/jeps/395) called '**Records**', which was finalized in **Java 16**. Records provide a succinct and expressive way to define immutable data carriers, significantly reducing the amount of boilerplate code.

In this blog, we’ll explore how [Java Records](https://docs.oracle.com/en/java/javase/17/language/records.html) can enhance your test automation, making your code more concise, readable, and maintainable. We will also compare Records with [Lombok](https://projectlombok.org/), a popular library used for reducing boilerplate code in earlier Java versions.

*For different examples of using Java Records with real-time APIs in test automation, you can check out my GitHub project* [*here*](https://github.com/rakesh-vardan/java-records-lombok)*.*

## Understanding Java Records

Java Records are a special kind of class in Java designed to act as immutable data carriers. They can be thought of as nominal tuples, containing immutable fields where the compiler automatically generates methods like `equals()`, `hashCode()`, `toString()`, and accessors. This makes Records particularly suited for use cases where you need to store and retrieve data without much additional logic.

### **Key Features of Java Records:**

* **Immutability**: Fields in Records are final, ensuring that the data can't be changed once created.
    
* **Generated Methods**: The Java compiler automatically provides implementations for `equals()`, `hashCode()`, and `toString()` methods.
    
* **Accessor Methods**: Instead of traditional getters, Records use accessor methods that match the field names.
    

### **Example of Java Record**

Here’s a simple example of a `User` record:

```java
public record User(String username, String password) {}
```

This one line of code is equivalent to writing a traditional class with private final fields, a constructor, and several utility methods like `equals()` and `hashCode()`.

## Why Use Java Records in Test Automation?

Test automation often involves setting up test data models, such as user credentials, API requests, or database entities. These models tend to be simple data holders without much business logic, making them perfect candidates for Records.

### **Benefits of Java Records in Test Automation:**

* **Reduced Boilerplate**: Simplifies the code by auto-generating common methods, eliminating the need for manual coding of accessors and other methods.
    
* **Improved Readability**: Compact syntax helps focus on the logic of the test rather than unnecessary class definitions.
    
* **Ease of Maintenance**: Less code means fewer points of failure and easier maintenance over time.
    

### **Example: Simplifying Test Code**

Let’s consider a test scenario for verifying login functionality. Here’s how you might implement it without using Records:

#### Without Records

```java
public class User {
    private final String username;
    private final String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}
```

And use it in a test like this:

```java
@Test
public void testLogin() {
    User user = new User("testuser", "testpassword");
    loginPage.login(user.getUsername(), user.getPassword());
    assertTrue(dashboardPage.isLoggedIn());
}
```

#### With Records

By using Records, the code becomes much more concise:

```java
public record User(String username, String password) {}
```

And use it in a test like this:

```java
@Test
public void testLogin() {
    User user = new User("testuser", "testpassword");
    loginPage.login(user.username(), user.password());
    assertTrue(dashboardPage.isLoggedIn());
}
```

In this case, the test logic remains the same, but the code is simplified, making it easier to read and maintain.

## Real-World Example: Testing an E-commerce Application

Consider a more complex test scenario in which we’re testing an e-commerce application. Here, we need to represent a `Order` containing an order ID, user details, and a list of items.

**Without Records**

Without using Records (or Lombok), the `Order` class might look like this:

```java
public class Order {
    private final String id;
    private final User user;
    private final List<Item> items;

    public Order(String id, User user, List<Item> items) {
        this.id = id;
        this.user = user;
        this.items = items;
    }

    public String getId() {
        return id;
    }

    public User getUser() {
        return user;
    }

    public List<Item> getItems() {
        return items;
    }
}
```

**With Records**

Using Records simplifies this model dramatically:

```java
public record Order(String id, User user, List<Item> items) {}
```

In a test, you could then represent and work with `Order` like this:

```java
@Test
public void testOrderProcessing() {
    User user = new User("testuser", "testpassword");
    List<Item> items = Arrays.asList(new Item("item1", 2), new Item("item2", 1));
    Order order = new Order("order1", user, items);
    
    orderPage.placeOrder(order);
    assertTrue(orderPage.isOrderPlaced(order.id()));
}
```

## Comparing Java Records with Lombok

Before Java [Records](https://docs.oracle.com/en/java/javase/17/language/records.html) were introduced, developers often turned to [Lombok](https://projectlombok.org/) to reduce boilerplate code. Lombok provides annotations like `@Data`, `@Getter`, and `@Setter`, which automatically generates common methods at compile time. However, Records, being a built-in language feature, come with their advantages and trade-offs.

### Key Differences:

* **Java Version**:
    
    * **Records**: Available from Java 16 onward.
        
    * **Lombok**: Compatible with older Java versions.
        
* **Immutability**:
    
    * **Records**: Fields are inherently final, ensuring immutability.
        
    * **Lombok**: Can generate mutable objects unless explicitly marked with `@Value` for immutability.
        
* **Dependencies**:
    
    * **Records**: Native to Java—no external dependencies.
        
    * **Lombok**: Requires adding an external library to your project.
        
* **Tooling Support**:
    
    * **Records**: Supported by most modern IDEs and tools since they are part of the core Java language.
        
    * **Lombok**: Some IDEs may have compatibility issues due to Lombok's bytecode manipulation.
        

### When to Use Records vs. Lombok:

* **Use Records if**:
    
    * You're using Java 16 or later.
        
    * You need simple, immutable data carriers with minimal custom behaviour.
        
* **Use Lombok if**:
    
    * You’re working with older Java versions.
        
    * You need mutable objects or more flexibility in the generated code.
        

## Conclusion

Java Records are a powerful tool for test automation, especially when working with simple, immutable data models. They reduce boilerplate code, enhance readability, and make tests easier to maintain. While Lombok remains a great option for those using earlier Java versions or needing additional flexibility, Records provides a cleaner, more efficient approach for modern Java applications.

When writing test automation in Java 16 or later, consider using Records to simplify your data model representations and improve the overall maintainability of your codebase.
