Search code examples
spockjmockit

How to use jmockit in Spock to test static methods to return multiple different values?


I want to use jmockit to test the static method in Spock, and combine the where tag to achieve different values of each mock to test different business logic. I tried a lot of writing methods, but they all failed. I hope I can get help or suggestions here. Thank you very much

Here is an example of my business code:

public class MyUtils {
    public static int staticMethod(int origin) {
        return 0;
    }
}
public class MyClass {    
    public void verify(int origin) {
        if (MyUtils.staticMethod(origin) == 1) {
            System.out.println("1");
        }
        if (MyUtils.staticMethod(origin) == 2) {
            System.out.println("2");
        }
        ...
    }
}

This is my Spock test code:

def "verify"() {
    when:
    myClass.verify(0)

    then:
    true

    where:
    mock | _
    mockStatic(1) | _
    mockStatic(2) | _
}

def mockStatic(val){
    new MockUp<MyUtils>() {
        @Mock
        public int staticMethod(int origin) {
            return val
        }
    }
}

I know that power can implement such a function, but because our team has been using jmockit, we want to know whether jmockit can implement such multiple different values of mock in Spock?


Solution

  • Put your method call into a closure and evaluate the closure during each iteration:

    package de.scrum_master.stackoverflow.q67882559
    
    import mockit.Mock
    import mockit.MockUp
    import mockit.internal.state.SavePoint
    import spock.lang.Requires
    import spock.lang.Specification
    import spock.lang.Unroll
    
    class StaticMethodJMockitTest extends Specification {
      def jMockitSavePoint = new SavePoint()
    
      def cleanup() {
        jMockitSavePoint.rollback()
      }
    
      @Unroll
      def "verify"() {
        given:
        mockClosure()
        MyClass myClass = new MyClass()
    
        when:
        myClass.verify(0)
    
        then:
        true
    
        where:
        mockClosure << [
          { /* no mock */ },
          { mockStatic(1) },
          { mockStatic(2) }
        ]
      }
    
      def mockStatic(val) {
        new MockUp<MyUtils>() {
          @Mock
          int staticMethod(int origin) {
            return val
          }
        }
      }
    
      public static class MyUtils {
        public static int staticMethod(int origin) {
          return 0;
        }
      }
    
      public static class MyClass {
        public void verify(int origin) {
          if (MyUtils.staticMethod(origin) == 1) {
            System.out.println("1");
          }
          if (MyUtils.staticMethod(origin) == 2) {
            System.out.println("2");
          }
        }
      }
    }
    

    If you wish to use data tables, you need to help the parser a bit by explicitly adding it -> inside in the closure, if the closure is in the first column of the data table. You can also use some nice naming for your unrolled iterations:

      @Unroll
      def "verify #description"() {
        given:
        mockClosure()
        MyClass myClass = new MyClass()
    
        when:
        myClass.verify(0)
    
        then:
        true
    
        where:
        description     | mockClosure
        "no mock"       | { /* no mock */ }
        "mock result 1" | { mockStatic(1) }
        "mock result 2" | { mockStatic(2) }
      }
    

    The reason for creating and rolling back the save point is that JMockit does not play nice with Spock concerning mock lifecycles and the maintainer has no intention to even think about helping. See JMockit issue #668 for more info.