Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
71 views
in Technique[技术] by (71.8m points)

java - Groovy: How to mock imported class and its methods

I have Groovy PSL project and I would like to add tests for code that calls outside classes. So, for example, I would like to mock this DeploymentMetrics class and it's methods so it doesn't call real class. All I want is to verify it was called.

CICDTest.groovy

import com.lesfurets.jenkins.unit.BaseRegressionTest
import org.junit.Before
import org.junit.Test

class CICDTest extends BaseRegressionTest {
    def serviceVersion = "12345rds234"
    String team = "TEAM"
    String service = "SERVICE"
    String deployEnv = "Development"
    String deployType = "App Code Deployment"
    String deployReason = "Routing Change"

    @Override
    @Before
    void setUp() throws Exception {
        super.setUp()
        callStackPath = "test/vars/callstacks/"
        binding.setProperty('steps', 'some')
        binding.setVariable('env', [getEnvironment: {[:]}])
    }

    @Test
    void publishCdMetricsSuccess() throws Exception {
        cicd = loadScript('vars/cicd.groovy')
        cicd.publishCdMetrics(serviceVersion, team, service, deployEnv, deployType, deployReason)
        printCallStack()
    }
}

/vars/cicd.groovy

import spg.dora.cd.DeploymentMetrics
def publishCdMetrics(serviceVersion, String team, String service, String deployEnv, String deployType, String deployReason) {
    try {
        String[] deployRegions = ['us-west-2'] as String[]
        DeploymentMetrics metricsHandler = new DeploymentMetrics(steps, env, docker)
        metricsHandler.postMetric(team, service, serviceVersion, deployEnv, deployType, deployReason, deployRegions)
    } catch (Exception exception) {
        error "Failed to publish CD metrics: ${exception}"
    }
}

DeploymentMetrics.groovy <- not owned by me (cannot modify)

class DeploymentMetrics implements Serializable {
    DeploymentMetrics(steps, env, docker) {
        this.steps = steps
        this.env = env
        this.common_shell = new CommonShell(steps, env)
    }

    def postMetric(String team, String service, String serviceVersion, String environment, String category, String reason, String[] awsRegions) {
      ...
      //doesn't return anything
    }
}

I tried new MockFor() following this example:

def mockDeploymentMetrics = new MockFor(DeploymentMetrics, true)
mockDeploymentMetrics.demand.with {
     DeploymentMetrics(any(), any(), any()) {
         new DeploymentMetrics(null, null, null)
     }
     postMetric { }
}
mockDeploymentMetrics.use {
     cicd = loadScript('vars/cicd.groovy')
     cicd.publishCdMetrics(serviceVersion, team, service, deployEnv, deployType, deployReason)
}

I tried different combinations of Mock and then PowerMock

@PrepareForTest(DeploymentMetrics.class)
class CICDTest extends BaseRegressionTest {
    @Override
    @Before
    void setUp() throws Exception {
       super.setUp()
       //deploymentMetrics = mock(DeploymentMetrics.class)
       deploymentMetrics = PowerMockito.mock(DeploymentMetrics.class)
       when(deploymentMetrics.postMetric(any(), any(), any(), any(), any(),any(), any())).thenReturn(new Object())
   }
}

I tried metaClass.constructor

DeploymentMetrics.metaClass.constructor = { steps, env, docker ->
   def constructor = DeploymentMetrics.class.getConstructor(Object.class,Object.class,Object.class)
   def instance = constructor.newInstance(any(), any(), any())

I tried instantiating class and spying on that object following this example:

DeploymentMetrics deploymentMetrics = new DeploymentMetrics(binding.getVariable("steps"), binding.getVariable("env"), binding.getVariable("docker"))
deploymentMetricsSpy = spy(deploymentMetrics)
doReturn(new Object()).when(deploymentMetricsMock).postMetric(anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any())

But it is always going inside real class and it's method. I actually do not want it to ever go inside real constructor, cause it calls other classes' constructors and it fails. So I want to avoid doing new on it completely.

Note: DeploymentMetrics only has 1 constructor that takes 3 Object parameters

I followed many examples out there, but nothing worked.

Can someone shed some light?

question from:https://stackoverflow.com/questions/65946672/groovy-how-to-mock-imported-class-and-its-methods

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Regarding the PowerMock version: to mock a constructor, you should PrepareForTests the class in which the constructor is called, not the class being constructed. You need to use the PowerMockRunner as well. Try changing:

@PrepareForTest(DeploymentMetrics.class)
class CICDTest extends BaseRegressionTest {
...
}

to

@RunWith(PowerMockRunner.class)
@PrepareForTest(cicd.class)
class CICDTest extends BaseRegressionTest {

    @Override
    @Before
    void setUp() throws Exception {
       deploymentMetrics = mock(DeploymentMetrics.class)
       PowerMockito.whenNew(DeploymentMetrics.class).withAnyArguments().thenReturn(deploymentMetrics)
       when(deploymentMetrics.postMetric(any(), any(), any(), any(), any(),any(), any())).thenReturn(new Object())
   }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...