How to mock a String using mockito?

后端 未结 15 1749
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-05 00:22

I need to simulate a test scenario in which I call the getBytes() method of a String object and I get an UnsupportedEncodingException.

I have tried to achie

相关标签:
15条回答
  • 2021-02-05 00:55

    The problem is the String class in Java is marked as final, so you cannot mock is using traditional mocking frameworks. According to the Mockito FAQ, this is a limitation of that framework as well.

    0 讨论(0)
  • 2021-02-05 00:58

    Perhaps A.f(String) should be A.f(CharSequence) instead. You can mock a CharSequence.

    0 讨论(0)
  • 2021-02-05 00:59

    If you can use JMockit, look at Rogério answer.

    If and only if your goal is to get code coverage but not actually simulate what missing UTF-8 would look like at runtime you can do the following (and that you can't or don't want to use JMockit):

    public static String f(String str){
        return f(str, "UTF-8");
    }
    
    // package private for example
    static String f(String str, String charsetName){
        try {
            return new String(str.getBytes(charsetName));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
        }
    }
    
    public class TestA {
    
        @Test(expected=IllegalArgumentException.class)
        public void testInvalid(){
            A.f(str, "This is not the encoding you are looking for!");
        }
    
        @Test
        public void testNormal(){
            // TODO do the normal tests with the method taking only 1 parameter
        }
    }
    
    0 讨论(0)
  • 2021-02-05 01:01

    instead of mocking string inject string value at setup method as below

    public void setup() throws IllegalAccessException {
        FieldUtils.writeField(yourTestClass, "stringVariableName", "value", true);
    }
    
    0 讨论(0)
  • 2021-02-05 01:02

    You can also use PowerMock's Mockito extension to mock final classes/methods even in system classes such as String. However I would also advice against mocking getBytes in this case and rather try to setup your expectation so that a real is String populated with the expected data is used instead.

    0 讨论(0)
  • 2021-02-05 01:03

    From its documentation, JDave can't remove "final" modifiers from classes loaded by the bootstrap classloader. That includes all JRE classes (from java.lang, java.util, etc.).

    A tool that does let you mock anything is JMockit.

    With JMockit, your test can be written as:

    import java.io.*;
    import org.junit.*;
    import mockit.*;
    
    public final class ATest
    {
       @Test(expected = UnsupportedOperationException.class)
       public void test() throws Exception
       {
          new Expectations()
          {
             @Mocked("getBytes")
             String aString;
    
             {
                aString.getBytes(anyString);
                result = new UnsupportedEncodingException("Parsing error.");
             }
          };
    
          A.f("test");
       }
    }
    

    assuming that the complete "A" class is:

    import java.io.*;
    
    public final class A
    {
       public static String f(String str)
       {
          try {
             return new String(str.getBytes("UTF-8"));
          }
          catch (UnsupportedEncodingException e) {
             throw new UnsupportedOperationException(e);
          }
       }
    }
    

    I actually executed this test in my machine. (Notice I wrapped the original checked exception in a runtime exception.)

    I used partial mocking through @Mocked("getBytes") to prevent JMockit from mocking everything in the java.lang.String class (just imagine what that could cause).

    Now, this test really is unnecessary, because "UTF-8" is a standard charset, required to be supported in all JREs. Therefore, in a production environment the catch block will never be executed.

    The "need" or desire to cover the catch block is still valid, though. So, how to get rid of the test without reducing the coverage percentage? Here is my idea: insert a line with assert false; as the first statement inside the catch block, and have the Code Coverage tool ignore the whole catch block when reporting coverage measures. This is one of my "TODO items" for JMockit Coverage. 8^)

    0 讨论(0)
提交回复
热议问题