Open hetzijzo opened 2 years ago
Hi @hetzijzo, Thanks for reporting the issue we'll take a look.
Unfortunately, we cannot fix this issue until support for Lombok annotations and types are added to the rewrite framework.
Good morning @pway99, is there any estimate to have the problem with the Lombok library solved?
Good Morning @josemariavillar, It's a tricky problem that will require a significant effort; at this time, we do not have an estimate.
I'm trying to figure out to find a workaround. The case is the following tree of recipes:
Changes have been made to .....java by: org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6 org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5 org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4 org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration org.openrewrite.java.testing.junit5.JUnit4to5Migration org.openrewrite.java.testing.junit5.UseMockitoExtension org.openrewrite.java.testing.mockito.Mockito1to3Migration org.openrewrite.java.testing.mockito.CleanupMockitoImports
I have tried the following workarounds with little success
Are there any other options?
Hello @hetzijzo, That's a good question, I am not aware of a good workaround for Lombok issues at this time. That said in the spirit of do no harm each recipe should do its best to guard against making a breaking change due to invalid type information.
I took another look at the UseDiamondOperators
recipe and was able to guard it against NewClass initializers associated with VariableDeclarations having a null or unknown type.
Are you able to provide some more information or create an issue for the CleanupMockitoImports
problems your encountering?
reverted accidental commit to the wrong branch
I have the following two Java files in a project
import lombok.data;
@Data
class MyObject {
String someField;
}
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class MyObjectTest {
@Mock
MyObject myObject;
@Test
void test() {
when(myObject.getSomeField()).thenReturn("testValue");
......
}
}
Now when I run the following command it will lead to a unwanted deleted static import of import static org.mockito.Mockito.when
. The project will no longer compile.
The reason for the delete is that the Lombok annotation processor will normally add the getter & setter of the someField
which is not detected or taken care of by the AST of openrewrite.
mvn org.openrewrite.maven:rewrite-maven-plugin:4.24.0:run -Dorg.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6 -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-java-security:1.11.0,org.openrewrite.recipe:rewrite-kubernetes:1.17.0,org.openrewrite.recipe:rewrite-logging-frameworks:1.7.0,org.openrewrite.recipe:rewrite-migrate-java:1.6.0,org.openrewrite.recipe:rewrite-spring:4.21.0,org.openrewrite.recipe:rewrite-testing-frameworks:1.22.0
The execution of openrewrite leads to the following change:
Changes have been made to MyObjectTest .java by:
org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6
org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5
org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4
org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration
org.openrewrite.java.testing.junit5.JUnit4to5Migration
org.openrewrite.java.testing.junit5.UseMockitoExtension
org.openrewrite.java.testing.mockito.Mockito1to3Migration
org.openrewrite.java.testing.mockito.CleanupMockitoImports
Hi @hetzijzo, Thanks for the additional info on the CleanupMockitoImports
recipe, I have updated it to be more defensive against removing imports for potential Mockito method invocations having null or missing type information. https://github.com/openrewrite/rewrite-testing-frameworks/pull/228
Please don't hesitate to share any other issues you may find :)
Think I might have a start at replacing lombok.val
, although there might be some limitations that I'm unaware about. Feel free to critique my approach. I figured since lombok.val
is "just an interface" when parsing without Lombok support, we might be able to cross off that one already.
Re-opening since https://github.com/openrewrite/rewrite-migrate-java/pull/124 does not actually provide rewrite lombok annotation support.
I still run into a lot issues with Lombok & OpenRewrite when one of the rules start upgrading JUnit 4 to JUnit 5 or JUnit asserts to AssertJ asserts. The lombok generated methods are not recognized and therefore imports are removed all over the place.
I guess many more teams would have trouble with running OpenRewrite on a project that uses lombok. What are the plans for teams using lombok?
Hi @hetzijzo ! I guess full support for Lombok is some way off, but perhaps there's something we can do already to prevent the imports from being removed; do you have a minimal example where you see imports being removed? That way we can try to capture that in a JUnit test and iterate more quickly.
Running the following command
mvn org.openrewrite.maven:rewrite-maven-plugin:4.39.0:run -Drewrite.activeRecipes=nl.fictive.bsp.UpgradeBspCoreLatest -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-java-security:1.21.0,org.openrewrite.recipe:rewrite-kubernetes:1.27.0,org.openrewrite.recipe:rewrite-logging-frameworks:1.17.0,org.openrewrite.recipe:rewrite-migrate-java:1.16.0,org.openrewrite.recipe:rewrite-spring:4.32.0,org.openrewrite.recipe:rewrite-testing-frameworks:1.33.0,nl.fictive.bsp:bsp-core-openrewrite-rules:7.7.1 -Drewrite.exclusions=**api**.yaml
On the folliwing Java file:
package nl.fictive.bsp.capability.serviceinzicht.service.client.notificatiebeheer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import ch.qos.logback.classic.Level;
import java.util.Date;
import java.util.UUID;
import nl.fictive.bsp.capability.serviceinzicht.service.application.mortgageinquiry.cleaning.CleanupInquiryEvent;
import nl.fictive.bsp.capability.serviceinzicht.util.logging.MemoryAppender;
import nl.fictive.bsp.capability.notificatiebeheer.api.NotificatiesVersturenApi;
import nl.fictive.bsp.capability.notificatiebeheer.api.model.Notificatie;
import nl.fictive.bsp.core.identity.Bank;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestClientException;
import org.wildfly.common.Assert;
@ExtendWith(MockitoExtension.class)
public class NotificatiebeheerServiceClientTest {
@InjectMocks private NotificatiebeheerServiceClient client;
@Mock private NotificatiesVersturenApi notificatiesVersturenApi;
@Captor ArgumentCaptor<Notificatie> notificatie;
@Test
public void notifyByMail_ExceptionThrown() {
// Arrange
final MemoryAppender memoryAppender = new MemoryAppender(NotificatiebeheerServiceClient.class);
UUID uuid = UUID.randomUUID();
CleanupInquiryEvent event =
CleanupInquiryEvent.builder()
.batchRun(new Date())
.message("TestInquiryEvent")
.successful(true)
.inquiryId(uuid)
.build();
doThrow(new RestClientException("test error message"))
.when(notificatiesVersturenApi)
.addNotificatie(eq(Bank.SOME.name().toLowerCase()), any(Notificatie.class));
ReflectionTestUtils.setField(client, "emailAddress", "tester@fictive.nl");
// Act
client.notifyByMail(event);
// Assert
assertEquals(1, memoryAppender.countEventsForLogger("NotificatiebeheerServiceClient"));
Assert.assertTrue(
memoryAppender.contains(
"Something went wrong while sending e-mail notification to tester@fictive.nl",
Level.ERROR));
}
@Test
public void notifyByMail() {
// Arrange
UUID uuid = UUID.randomUUID();
CleanupInquiryEvent event =
CleanupInquiryEvent.builder()
.batchRun(new Date())
.message("TestInquiryEvent")
.successful(true)
.inquiryId(uuid)
.build();
// Act
client.notifyByMail(event);
// Assert
verify(notificatiesVersturenApi).addNotificatie(eq("SOME"), notificatie.capture());
assertTrue(notificatie.getValue().getNotificatieTypes().contains("EMAIL"));
assertTrue(
notificatie.getValue().getOnderwerp().equals("Mortgage Inquiry Cleanup Scheduler Warning"));
assertTrue(
notificatie
.getValue()
.getInhoud()
.startsWith("Voor beheer van"));
assertTrue(notificatie.getValue().getInhoud().contains(uuid.toString()));
assertTrue(notificatie.getValue().getInhoud().contains("SCHEDULED_CLEANUP_INQUIRY"));
assertTrue(notificatie.getValue().getInhoud().contains("TestInquiryEvent"));
}
}
Results with the following in the log:
[WARNING] Changes have been made to src\test\java\nl\fictive\bsp\capability\serviceinzicht\service\client\notificatiebeheer\NotificatiebeheerServiceClientTest.java by:
[WARNING] nl.fictive.bsp.UpgradeBspCoreLatest
[WARNING] nl.fictive.bsp.UpgradeBspCore7
[WARNING] nl.fictive.bsp.UpgradeBspCore6
[WARNING] org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_6
[WARNING] org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_5
[WARNING] org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_4
[WARNING] org.openrewrite.java.testing.junit5.JUnit5BestPractices
[WARNING] org.openrewrite.java.testing.junit5.StaticImports
[WARNING] org.openrewrite.java.UseStaticImport: {methodPattern=org.junit.jupiter.api.Assertions *(..)}
[WARNING] org.openrewrite.java.testing.cleanup.TestsShouldNotBePublic
And the file is changed like this:
package nl.fictive.bsp.capability.serviceinzicht.service.client.notificatiebeheer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import ch.qos.logback.classic.Level;
import java.util.Date;
import java.util.UUID;
import nl.fictive.bsp.capability.serviceinzicht.service.application.mortgageinquiry.cleaning.CleanupInquiryEvent;
import nl.fictive.bsp.capability.serviceinzicht.util.logging.MemoryAppender;
import nl.fictive.bsp.capability.notificatiebeheer.api.NotificatiesVersturenApi;
import nl.fictive.bsp.capability.notificatiebeheer.api.model.Notificatie;
import nl.fictive.bsp.core.identity.Bank;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.client.RestClientException;
import org.wildfly.common.Assert;
@ExtendWith(MockitoExtension.class)
class NotificatiebeheerServiceClientTest {
@InjectMocks
private NotificatiebeheerServiceClient client;
@Mock
private NotificatiesVersturenApi notificatiesVersturenApi;
@Captor
ArgumentCaptor<Notificatie> notificatie;
@Test
void notifyByMail_ExceptionThrown() {
// Arrange
final MemoryAppender memoryAppender = new MemoryAppender(NotificatiebeheerServiceClient.class);
UUID uuid = UUID.randomUUID();
CleanupInquiryEvent event =
CleanupInquiryEvent.builder()
.batchRun(new Date())
.message("TestInquiryEvent")
.successful(true)
.inquiryId(uuid)
.build();
doThrow(new RestClientException("test error message"))
.when(notificatiesVersturenApi)
.addNotificatie(eq(Bank.SOME.name().toLowerCase()), any(Notificatie.class));
ReflectionTestUtils.setField(client, "emailAddress", "vobankfinancieeladvies@fictive.nl");
// Act
client.notifyByMail(event);
// Assert
assertEquals(1, memoryAppender.countEventsForLogger("NotificatiebeheerServiceClient"));
Assert.assertTrue(
memoryAppender.contains(
"Something went wrong while sending e-mail notification to vobankfinancieeladvies@fictive.nl",
Level.ERROR));
}
@Test
void notifyByMail() {
// Arrange
UUID uuid = UUID.randomUUID();
CleanupInquiryEvent event =
CleanupInquiryEvent.builder()
.batchRun(new Date())
.message("TestInquiryEvent")
.successful(true)
.inquiryId(uuid)
.build();
// Act
client.notifyByMail(event);
// Assert
verify(notificatiesVersturenApi).addNotificatie(eq("SOME"), notificatie.capture());
assertTrue(notificatie.getValue().getNotificatieTypes().contains("EMAIL"));
assertTrue(
notificatie.getValue().getOnderwerp().equals("Mortgage Inquiry Cleanup Scheduler Warning"));
assertTrue(
notificatie
.getValue()
.getInhoud()
.startsWith("Voor beheer van some-service-inzicht"));
assertTrue(notificatie.getValue().getInhoud().contains(uuid.toString()));
assertTrue(notificatie.getValue().getInhoud().contains("SCHEDULED_CLEANUP_INQUIRY"));
assertTrue(notificatie.getValue().getInhoud().contains("TestInquiryEvent"));
}
}
It misses the import of import static org.junit.jupiter.api.Assertions.assertTrue;
@timtebeek I hope this helps a little
And just to be sure; the release this morning hasn't helped right?
I tried the command with all the latest components released this morning with the same result.
@hetzijzo I wonder if this example has anything to do with Lombok. In the provided example class there are no Lombok annotations (or imports). The only unusual thing I can see is that org.wildfly.common.Assert
is being imported and then used in a Assert.assertTrue()
statement. I assume this is accidental. I tried to reproduce the problem with this lead but failed.
Can you check if the problem is due to this import? Is the full source code (with all dependencies) available on GitHub or can you provide a reproducer? That would really help to get this issue fixed.
It's been a long time coming but I think I have an approach that will work for this: https://github.com/openrewrite/rewrite/compare/main...lombok-spike
After further experimentation we've identified a number of shortcomings in the current lombok support. Some of it pretty substantial, so I'm going to re-open this while I work through it.
Here's roughly what still needs to happen:
@Data
and @Value
org.openrewrite.java.cleanup.ExplicitInitialization
andorg.openrewrite.java.cleanup.UseDiamondOperator
removes the argument type when it's a lombok.val variable.val products = new ArrayList<Product>();
val products = new ArrayList<>();
which is incorrect and doesn't compile anymore.