Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EasyMock to Mockito #454

Open
jpraet opened this issue Jan 7, 2024 · 5 comments
Open

EasyMock to Mockito #454

jpraet opened this issue Jan 7, 2024 · 5 comments
Labels
good first issue Good for newcomers recipe Recipe request

Comments

@jpraet
Copy link

jpraet commented Jan 7, 2024

What problem are you trying to solve?

Migrate from EasyMock to Mockito.

Resources:

@timtebeek timtebeek added the recipe Recipe request label Jan 8, 2024
@timtebeek
Copy link
Contributor

Hi @jpraet ! Thanks for the suggestion; looking at the gist link this appears relatively straightforward:

  sed -i 's/org.easymock.EasyMock.createNiceMock/org.mockito.Mockito.mock/g' $item
  sed -i 's/org.easymock.EasyMock.createMock/org.mockito.Mockito.mock/g' $item
  sed -i 's/org.easymock.EasyMock.expect/org.mockito.Mockito.when/g' $item
  sed -i 's/org.easymock.EasyMock/org.mockito.Mockito/g' $item
  sed -i 's/EasyMock/Mockito/g' $item
  sed -i 's/createNiceMock/mock/g' $item
  sed -i 's/createMock/mock/g' $item
  sed -i 's/expect(/when(/g' $item
  sed -i 's/andReturn/thenReturn/g' $item
  sed -i 's/\.anyTimes()//g' $item
  sed -i '/replay[^A-Za-z0-9]/d' $item

At first glance most, if not all of these can be converted to use recipes we already have which you can compose in our recipe builder at https://app.moderne.io/recipes/builder
For instance the first conversion could be replaced by

type: specs.openrewrite.org/v1beta/recipe
name: java.testing.easymock.EasyMockToMockito
displayName: Migrate from EasyMock to Mockito
description: This recipe will apply changes commonly needed when migrating from EasyMock to Mockito.
recipeList:
  - org.openrewrite.java.ChangeMethodTargetToStatic:
      methodPattern: org.easymock.EasyMock createNiceMock(..)
      fullyQualifiedTargetTypeName: org.mockito.Mockito
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito createNiceMock(..)
      newMethodName: mock
...

We already have a very similar recipe for JMockit: https://github.com/openrewrite/rewrite-testing-frameworks/blob/main/src/main/resources/META-INF/rewrite/jmockit.yml

Would you be willing to create a first draft of these migration recipes to get this started?

@dblackhall-tyro
Copy link

dblackhall-tyro commented Oct 11, 2024

type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.testing.easymock.EasyMockToMockito
displayName: Migrate from EasyMock to Mockito
description: This recipe will apply changes commonly needed when migrating from EasyMock to Mockito.
tags:
  - testing
  - easymock
recipeList:
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IExpectationSetters times(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IExpectationSetters once()
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IExpectationSetters atLeastOnce()
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IExpectationSetters anyTimes()
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andReturn(..)
      newMethodName: thenReturn
      ignoreDefinition: true
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andThrow(java.lang.Throwable)
      newMethodName: thenThrow
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andAnswer(..)
      newMethodName: thenAnswer
      ignoreDefinition: true
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andStubReturn(..)
      newMethodName: thenReturn
      ignoreDefinition: true
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andStubThrow(java.lang.Throwable)
      newMethodName: thenThrow
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.easymock.IExpectationSetters andStubAnswer(..)
      newMethodName: thenAnswer
      ignoreDefinition: true
  - org.openrewrite.java.ChangeMethodTargetToStatic:
      methodPattern: org.easymock.IMocksControl mock(..)
      fullyQualifiedTargetTypeName: org.mockito.Mockito
  - org.openrewrite.java.ChangeMethodTargetToStatic:
      methodPattern: org.easymock.IMocksControl createMock(..)
      fullyQualifiedTargetTypeName: org.mockito.Mockito
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IMocksControl replay(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IMocksControl verify(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IMocksControl verifyRecording(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.IMocksControl verifyUnexpectedCalls(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.EasyMockSupport replayAll()
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.easymock.EasyMockSupport verifyAll()
#  - com.todo.openrewrite.java.easymock.RemoveExtendsEasyMockSupport
  - org.openrewrite.java.ChangeType:
      oldFullyQualifiedTypeName: org.easymock.EasyMock
      newFullyQualifiedTypeName: org.mockito.Mockito
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito createNiceMock(..)
      newMethodName: mock
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito createStrictMock(..)
      newMethodName: mock
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito createMock(..)
      newMethodName: mock
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito niceMock(..)
      newMethodName: mock
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito strictMock(..)
      newMethodName: mock
  - org.openrewrite.java.ReorderMethodArguments:
      methodPattern: org.mockito.Mockito mock(String, Class)
      newParameterNames:
        - classToMock
        - name
      oldParameterNames:
        - name
        - classToMock
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito reportMatcher(..)
      newMethodName: argThat
  - org.openrewrite.java.ChangeMethodName:
      methodPattern: org.mockito.Mockito expect(..)
      newMethodName: when
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.mockito.Mockito replay(..)
  - org.openrewrite.java.migrate.RemoveMethodInvocation:
      methodPattern: org.mockito.Mockito verify(..)
#  - com.todo.openrewrite.java.easymock.RefactorEasyMockIArgumentMatcherIntoMockitoArgumentMatcher
#  - com.todo.openrewrite.java.easymock.RewriteEasyMockExpectationsIntoMockitoVerify
  - org.openrewrite.java.dependencies.AddDependency:
      groupId: org.mockito
      artifactId: mockito-core
      version: 5.x
      onlyIfUsing: org.mockito.*
  - org.openrewrite.java.dependencies.RemoveDependency:
      groupId: org.easymock
      artifactId: easymock

There are some more complicated behaviours I have found in my codebase that will require some more complicated recipes e.g:

  • expectLastCall()
  • test classes that extend EasyMockSupport

But for a first draft, it does quite well :)

@dblackhall-tyro
Copy link

RemoveExtendsEasyMockSupport was easy enough (I just copied the bits I needed from MigrateJUnitTestCase)

public class RemoveExtendsEasyMockSupport extends Recipe {

    @Override
    public @NlsRewrite.DisplayName String getDisplayName() {
        return "Migrate Test classes that extend `org.easymock.EasyMockSupport` to use Mockito";
    }

    @Override
    public @NlsRewrite.Description String getDescription() {
        return "Modify test classes by removing extends EasyMockSupport and replacing EasyMock methods with Mockito equivalents.";
    }

    private static boolean isSupertypeTestCase(JavaType.@Nullable FullyQualified fullyQualified) {
        if (fullyQualified == null || fullyQualified.getSupertype() == null || "java.lang.Object".equals(fullyQualified.getFullyQualifiedName())) {
            return false;
        }

        JavaType.FullyQualified fqType = TypeUtils.asFullyQualified(fullyQualified);
        if (fqType != null && "org.easymock.EasyMockSupport".equals(fqType.getFullyQualifiedName())) {
            return true;
        }
        return isSupertypeTestCase(fullyQualified.getSupertype());
    }

    @Override
    public TreeVisitor<?, ExecutionContext> getVisitor() {
        return new EasyMockSupportVisitor();
    }

    private static class EasyMockSupportVisitor extends JavaIsoVisitor<ExecutionContext> {
        @Override
        public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
            if (!isSupertypeTestCase(classDecl.getType())) {
                return classDecl;
            }
            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
            if (cd.getExtends() != null && cd.getExtends().getType() != null) {
                JavaType.FullyQualified fullQualifiedExtension = TypeUtils.asFullyQualified(cd.getExtends().getType());
                if (fullQualifiedExtension != null && "org.easymock.EasyMockSupport".equals(fullQualifiedExtension.getFullyQualifiedName())) {
                    cd = cd.withExtends(null);
                }
            }
            maybeRemoveImport("org.easymock.EasyMockSupport");
            return cd;
        }
    }
}
class RemoveExtendsEasyMockSupportTest implements RewriteTest {

    @Override
    public void defaults(RecipeSpec spec) {
        spec.recipe(new RemoveExtendsEasyMockSupport());
    }

    @Test
    public void shouldRemoveEasyMockSupportParentClass() {
        rewriteRun(
          //language=java
            java(
                """
                package com.todo.openrewrite.easymock;
                
                import org.easymock.EasyMockSupport;
    
                public class MigrateEasyMockSupportTest extends EasyMockSupport {
                }
                """,
                """
                package com.tyro.openrewrite.easymock;
    
                public class MigrateEasyMockSupportTest {
                }
                """
            )
        );
    }

    @Test
    public void shouldLeaveClassesWithoutEasyMockSupportAlone() {
        rewriteRun(
          //language=java
            java(
                """
                package com.todo.openrewrite.easymock;
                
                public class MigrateEasyMockSupportTest {
                }
                """
            )
        );
    }

    @Test
    public void shouldLeaveClassesWithDifferentExtendsAlone() {
        rewriteRun(
          //language=java
          java(
            """
            package com.todo.openrewrite.easymock;
            
            import javax.management.monitor.Monitor;
            
            public class MigrateEasyMockSupportTest extends Monitor {
            }
            """
          )
        );
    }
}

@timtebeek
Copy link
Contributor

Thanks a lot for sharing @dblackhall-tyro ! Great to see this kicked off. Would you be willing to contribute these on a draft PR? That way you get credited as the recipe author in our docs. We can collaborate and iterate from there.

@timtebeek timtebeek added the good first issue Good for newcomers label Oct 11, 2024
@dblackhall-tyro
Copy link

Certainly! I've found a few more small cases over the last few days so I'll wrap them all up and create a PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers recipe Recipe request
Projects
Status: Recipes Wanted
Development

No branches or pull requests

3 participants