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

Discussion (0)