9

I'm testing some groovy code that uses a java library and I want to mock out the library calls because they use the network. So the code under test looks something like:

def verifyInformation(String information) {
    def request = new OusideLibraryRequest().compose(information)
    new OutsideLibraryClient().verify(request)
}

I tried using MockFor and StubFor but I get errors such as:

No signature of method: com.myproject.OutsideLibraryTests.MockFor() is applicable for argument types: (java.lang.Class) values: [class com.otherCompany.OusideLibraryRequest]  

I'm using Grails 2.0.3.

ataylor
  • 64,891
  • 24
  • 161
  • 189
user1394451
  • 93
  • 1
  • 3

2 Answers2

10

I've just found that we can always overwrite a constructor via MetaClass, as Grails 2 will be reset MetaClass modification at the end of each test.

This trick is better than Groovy's MockFor. AFAIK, Groovy's MockFor does not allow us to mock JDK's classes, java.io.File, for example. However in the below example, you cannot use File file = new File("aaa") as the real object type is a Map, not a File. The example is a Spock specification.

def "test mock"() {
    setup:
    def fileControl = mockFor(File)
    File.metaClass.constructor = { String name -> [name: name] }
    def file = new File("aaaa")

    expect:
    file.name == "aaaa"
}
chanwit
  • 3,174
  • 2
  • 23
  • 20
  • You may want to precede your test (or class) with `@ConfineMetaClassChanges([File])` which isolates the changes you make to `File`'s `metaClass`; see http://spockframework.org/spock/docs/1.1/all_in_one.html#_confinemetaclasschanges – Piran May 03 '18 at 14:15
6

The second, optional parameter to MockFor's constructor is interceptConstruction. If you set this to true, you can mock the constructor. Example:

import groovy.mock.interceptor.MockFor
class SomeClass {
    def prop
    SomeClass() {
        prop = "real"
    }
}

def mock = new MockFor(SomeClass, true)
mock.demand.with {
    SomeClass() { new Expando([prop: "fake"]) }
}
mock.use {
    def mockedSomeClass = new SomeClass()
    assert mockedSomeClass.prop == "fake"
}

Note, however, you can only mock out groovy objects like this. If you're stuck with a Java library, you can pull the construction of the Java object into a factory method and mock that.

ataylor
  • 64,891
  • 24
  • 161
  • 189
  • I was not able to find a single example on the web with class that has only constructor with parameters. Do you know how above example would look like for such case? – Angelina Jan 29 '21 at 23:30
  • I am trying to mock `metricsHandler = new DeploymentMetrics(steps, env, docker) metricsHandler.postMetric(team, service)` – Angelina Jan 29 '21 at 23:46
  • If I try: `def stub = new StubFor(DeploymentMetrics, true) stub.demand.with { DeploymentMetrics() {steps,env,docker -> new Expando(steps:steps, env:env, docker:docker)} postMetric() {a,b,c,d,e,f,g -> "from mock with proxy"} }` I got `Cannot cast object '{steps=some, env={getEnvironment=vars.Testingq$_setUp_closure1@7cd65eb1}, docker=com.lesfurets.jenkins.unit.DockerMock@54dfe857}' with class 'groovy.util.Expando' to class 'spg.dora.cd.DeploymentMetrics'` – Angelina Jan 29 '21 at 23:52