DEV Community

Franz Wong
Franz Wong

Posted on

Replace method with JavaParser library

Motivation

Recently I want to migrate from JUnit assertEquals to Hamcrest assertThat and is to improve readability of my unit tests.

Instead of using regular expression, it seems it is safer to parse the syntax and manipulate it, so I choose JavaParser.

Objective

Here is how existing test looks like.

package com.franzwong.playground;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class MyTest {
    @Test
    public void test() {
        String a = "123";
        assertEquals("123", a);
    }
}
Enter fullscreen mode Exit fullscreen mode

I would like to change it to something like this.

package com.franzwong.playground;

import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.is;

class MyTest {
    @Test
    public void test() {
        String a = "123";
        assertThat(a, is("123"));
    }
}

Enter fullscreen mode Exit fullscreen mode

Action items:

  1. Remove the static import of assertEquals
  2. Add static import for assertThat and is
  3. Convert assertEquals("123", a) to assertThat(a, is("123"))
  4. Save modified source

Solution

Add Maven dependency for JavaParser

<dependency>
    <groupId>com.github.javaparser</groupId>
    <artifactId>javaparser-core</artifactId>
    <version>3.16.2</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Setup CompilationUnit

Suppose MyTest.java file is stored in /tmp folder.

SourceRoot sourceRoot = new SourceRoot(Path.of("/tmp"));
CompilationUnit cu = sourceRoot.parse("", "MyTest.java");
Enter fullscreen mode Exit fullscreen mode

Remove the static import of assertEquals

We just need to walk through the syntax tree. If the import declaration node is found and it is the assertEquals node, we just remove it.

cu.walk(ImportDeclaration.class, e -> {
    if (e.isStatic() && "org.junit.jupiter.api.Assertions.assertEquals".equals(e.getNameAsString())) {
        e.remove();
    }
});
Enter fullscreen mode Exit fullscreen mode

Add static import for assertThat and is

Parameters of ImportDeclaration constructor

  • 1st parameter is the static method name
  • 2nd parameter is true if it is a static import
  • 3rd parameter is true if it is asterisk
cu.addImport(new ImportDeclaration("org.hamcrest.MatcherAssert.assertThat", true, false));
cu.addImport(new ImportDeclaration("org.hamcrest.CoreMatchers.is", true, false));
Enter fullscreen mode Exit fullscreen mode

Convert assertEquals("123", a) to assertThat(a, is("123"))

We walk through the syntax tree again. If it is a MethodCallExpr node and its name is assertEquals, we rename it and change its parameters.

cu.walk(MethodCallExpr.class, e -> {
  if (!"assertEquals".equals(e.getNameAsString())) {
      return;
  }

  // Rename method
  e.setName("assertThat");

  // Get the parameter list of `assertEquals`
  NodeList<Expression> equalsArgments = e.getArguments();

  // Create a new `is` method and assign 1st parameter of `assertEquals` to it
  MethodCallExpr isMethod = new MethodCallExpr("is", equalsArgments.get(0));

  // Change the parameters
  equalsArgments.set(0, equalsArgments.get(1));
  equalsArgments.set(1, isMethod);
});
Enter fullscreen mode Exit fullscreen mode

Save modified source

We just need to provide a folder path to it.

sourceRoot.saveAll(Path.of("/tmp/output"));
Enter fullscreen mode Exit fullscreen mode

So that's it. Here is how the whole source code looks like. I hope this tutorial is useful to you.

public static void main(String[] args) {

  SourceRoot sourceRoot = new SourceRoot(Path.of("/tmp"));
  CompilationUnit cu = sourceRoot.parse("", "MyTest.java");

  cu.walk(ImportDeclaration.class, e -> {
    if (e.isStatic() && "org.junit.jupiter.api.Assertions.assertEquals".equals(e.getNameAsString())) {
      e.remove();
    }
  });

    cu.addImport(new ImportDeclaration("org.hamcrest.MatcherAssert.assertThat", true, false));
    cu.addImport(new ImportDeclaration("org.hamcrest.CoreMatchers.is", true, false));

  cu.walk(MethodCallExpr.class, e -> {
    if (!"assertEquals".equals(e.getNameAsString())) {
      return;
    }

    // Rename method
    e.setName("assertThat");

    // Get the parameter list of `assertEquals`
    NodeList<Expression> equalsArgments = e.getArguments();

    // Create a new `is` method and assign 1st parameter of `assertEquals` to it
    MethodCallExpr isMethod = new MethodCallExpr("is", equalsArgments.get(0));

    // Change the parameters
    equalsArgments.set(0, equalsArgments.get(1));
    equalsArgments.set(1, isMethod);
  });

  sourceRoot.saveAll(Path.of("/tmp/output"));
}
Enter fullscreen mode Exit fullscreen mode

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay