简体   繁体   English

在不使用 powermock 的情况下模拟私有方法中使用的 static 方法

[英]Mock a static method used inside a private method without using powermock

I wanted to mock a static method used inside a private method of a class and return a specific value.我想模拟在 class 的私有方法中使用的 static 方法并返回特定值。

 public class Test {

    private String encodeValue(String abc) {

    try {
        return URLEncoder.encode(...);
    } catch (UnsupportedEncodingException e) {
        throw InvalidValueException.create("Error converting");
    }
}

URLEncoder.encode ->encode is a static method inside URLEncoder. URLEncoder.encode ->encode 是 URLEncoder 中的 static 方法。

In Test class using powermock works:在测试 class 使用 powermock 作品:

    PowerMock.mockStatic(URLEncoder.class);
    expect(URLEncoder.encode()).andThrow(new UnsupportedEncodingException());
    PowerMock.replay(URLEncoder.class);
    String encoded = Whitebox.invokeMethod(testMock,"encodeVaue","Apple Mango");

But i wanted to replace Powermock with any other mocking ways available.但我想用任何其他可用的 mocking 方式替换 Powermock。 Is there a way to mock the above class.有没有办法模拟上面的 class。

URL Encoder class: URL 编码器 class:

 /**
 * Translates a string into {@code application/x-www-form-urlencoded}
 * format using a specific encoding scheme. This method uses the
 * supplied encoding scheme to obtain the bytes for unsafe
 * characters.
 * <p>
 * <em><strong>Note:</strong> The <a href=
 * "http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars">
 * World Wide Web Consortium Recommendation</a> states that
 * UTF-8 should be used. Not doing so may introduce
 * incompatibilities.</em>
 *
 * @param   s   {@code String} to be translated.
 * @param   enc   The name of a supported
 *    <a href="../lang/package-summary.html#charenc">character
 *    encoding</a>.
 * @return  the translated {@code String}.
 * @exception  UnsupportedEncodingException
 *             If the named encoding is not supported
 * @see URLDecoder#decode(java.lang.String, java.lang.String)
 * @since 1.4
 */
public static String encode(String s, String enc)
    throws UnsupportedEncodingException {

    boolean needToChange = false;
    StringBuffer out = new StringBuffer(s.length());
    Charset charset;
    CharArrayWriter charArrayWriter = new CharArrayWriter();

    if (enc == null)
        throw new NullPointerException("charsetName");

    try {
        charset = Charset.forName(enc);
    } catch (IllegalCharsetNameException e) {
        throw new UnsupportedEncodingException(enc);
    } catch (UnsupportedCharsetException e) {
        throw new UnsupportedEncodingException(enc);
    }

    for (int i = 0; i < s.length();) {
        int c = (int) s.charAt(i);
        //System.out.println("Examining character: " + c);
        if (dontNeedEncoding.get(c)) {
            if (c == ' ') {
                c = '+';
                needToChange = true;
            }
            //System.out.println("Storing: " + c);
            out.append((char)c);
            i++;
        } else {
            // convert to external encoding before hex conversion
            do {
                charArrayWriter.write(c);
                /*
                 * If this character represents the start of a Unicode
                 * surrogate pair, then pass in two characters. It's not
                 * clear what should be done if a bytes reserved in the
                 * surrogate pairs range occurs outside of a legal
                 * surrogate pair. For now, just treat it as if it were
                 * any other character.
                 */
                if (c >= 0xD800 && c <= 0xDBFF) {
                    /*
                      System.out.println(Integer.toHexString(c)
                      + " is high surrogate");
                    */
                    if ( (i+1) < s.length()) {
                        int d = (int) s.charAt(i+1);
                        /*
                          System.out.println("\tExamining "
                          + Integer.toHexString(d));
                        */
                        if (d >= 0xDC00 && d <= 0xDFFF) {
                            /*
                              System.out.println("\t"
                              + Integer.toHexString(d)
                              + " is low surrogate");
                            */
                            charArrayWriter.write(d);
                            i++;
                        }
                    }
                }
                i++;
            } while (i < s.length() && !dontNeedEncoding.get((c = (int) s.charAt(i))));

            charArrayWriter.flush();
            String str = new String(charArrayWriter.toCharArray());
            byte[] ba = str.getBytes(charset);
            for (int j = 0; j < ba.length; j++) {
                out.append('%');
                char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16);
                // converting to use uppercase letter as part of
                // the hex value if ch is a letter.
                if (Character.isLetter(ch)) {
                    ch -= caseDiff;
                }
                out.append(ch);
                ch = Character.forDigit(ba[j] & 0xF, 16);
                if (Character.isLetter(ch)) {
                    ch -= caseDiff;
                }
                out.append(ch);
            }
            charArrayWriter.reset();
            needToChange = true;
        }
    }

    return (needToChange? out.toString() : s);
}

Mocking privates and statics is one of the chief strengths of JMockit over other mocking frameworks. Mocking 私有和静态是 JMockit 相对于其他 mocking 框架的主要优势之一。

The class you call "Test" is really the "ClassUnderTest", so apologies, but the test of "Test" is "TestTest":)您称为“Test”的class实际上是“ClassUnderTest”,所以很抱歉,但是“Test”的测试是“TestTest”:)

public class TestTest {
  @Tested
  public Test cut;

  @Test
  public void testencodeValue() {

     // Mock the static
     new MockUp<URLEncoder>() {
       @Mock
       String encode(String s, String enc) {
          return "JMockit FTW";
       }
     };

    // invoke the private method
        final Method method = MethodReflection.findCompatibleMethod(Test.class, "encodeValue", new Class<?>[] { String.class });
        final String res = MethodReflection.invoke(cut, method);
    
     assertEquals("JMockit FTW", res);
  }
}

That said, testing privates is sort of a PITA.也就是说,测试私人是一种 PITA。 I am generally of the mind that if a method is worth testing, it is almost certainly worth exposing.我通常认为,如果一种方法值得测试,那么几乎可以肯定它值得公开。 The same criteria that make the method worth testing means that somebody-somewhere-someday will want to override your implementation and provide a slightly alternative one.使该方法值得测试的相同标准意味着某天某个地方的某个人会想要覆盖您的实现并提供一个稍微替代的实现。 Make their job easy, and make it protected.让他们的工作变得轻松,并使其受到保护。 Make your (testing) job easy, and do the same thing.让您的(测试)工作变得轻松,并做同样的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM