Java 如何使用 mockito 模拟字符串?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1079239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:16:10  来源:igfitidea点击:

How to mock a String using mockito?

javaunit-testingmockingmockito

提问by Alceu Costa

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

我需要模拟一个测试场景,在该场景中我调用getBytes()String 对象的方法并得到 UnsupportedEncodingException。

I have tried to achieve that using the following code:

我尝试使用以下代码实现这一点:

String nonEncodedString = mock(String.class);
when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));

The problem is that when I run my test case I get a MockitoException that says that I can't mock a java.lang.String class.

问题是,当我运行我的测试用例时,我得到一个 MockitoException,说我不能模拟 java.lang.String 类。

Is there a way to mock a String object using mockito or, alternatively, a way to make my String object throw an UnsupportedEncodingException when I call the getBytes method?

有没有办法使用 mockito 来模拟 String 对象,或者在调用 getBytes 方法时使我的 String 对象抛出 UnsupportedEncodingException 的方法?



Here are more details to illustrate the problem:

这里有更多细节来说明问题:

This is the class that I want to test:

这是我要测试的类:

public final class A {
    public static String f(String str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

This is my testing class (I'm using JUnit 4 and mockito):

这是我的测试类(我使用的是 JUnit 4 和 mockito):

public class TestA {

    @Test(expected=UnsupportedEncodingException.class)
    public void test(){
        String aString = mock(String.class);
        when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));
        A.f(aString);
    }
}

采纳答案by Peter

The problem is the Stringclass 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.

问题是StringJava 中的类被标记为 final,因此您不能使用传统的模拟框架进行模拟。根据Mockito FAQ,这也是该框架的限制。

回答by Yishai

Mockito can't mock final classes. JMock, combined with a library from JDave can. Here are instructions.

Mockito 不能模拟最终课程。JMock,结合来自 JDave 的库可以。这是说明

JMock doesn't do anything special for final classes other than rely on the JDave library to unfinalize everything in the JVM, so you could experiment with using JDave's unfinalizer and see if Mockito will then mock it.

除了依赖 JDave 库来取消 JVM 中的所有内容之外,JMock 对 final 类没有做任何特殊的事情,因此您可以尝试使用 JDave 的取消终结器,看看 Mockito 是否会模拟它。

回答by duffymo

It is a project requirement that the unit tests coverage percentage must but higher than a given value. To achieve such percentage of coverage the tests must cover the catch block relative to the UnsupportedEncodingException.

单元测试覆盖率百分比必须高于给定值是项目要求。为了达到这样的覆盖率,测试必须覆盖相对于 UnsupportedEncodingException 的 catch 块。

What is that given coverage target? Some people would say that shooting for 100% coverage isn't always a good idea.

给定的覆盖目标是什么?有些人会说拍摄 100% 覆盖率并不总是一个好主意

Besides, that's no way to test whether or not a catch block was exercised. The right way is to write a method that causes the exception to be thrown and make observation of the exception being thrown the success criterion. You do this with JUnit's @Test annotation by adding the "expected" value:

此外,这也无法测试是否执行了 catch 块。正确的方法是编写一个方法,导致抛出异常,并以观察抛出的异常为成功标准。您可以使用 JUnit 的 @Test 批注通过添加“预期”值来执行此操作:

@Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() {
   new ArrayList<Object>().get(1);
}

回答by Rich Seller

Have you tried passing an invalid charsetName to getBytes(String)?

您是否尝试过将无效的 charsetName 传递给 getBytes(String)?

You could implement a helper method to get the charsetName, and override that method within your test to a nonsense value.

您可以实现一个辅助方法来获取 charsetName,并在您的测试中将该方法覆盖为一个无意义的值。

回答by Rogério

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.).

从其文档中,JDave 无法从引导类加载器加载的类中删除“final”修饰符。这包括所有 JRE 类(来自 java.lang、java.util 等)。

A tool that does let you mock anything is JMockit.

一个可以让你模拟任何东西的工具是JMockit

With JMockit, your test can be written as:

使用 JMockit,你的测试可以写成:

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:

假设完整的“A”类是:

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.Stringclass (just imagine what that could cause).

我使用部分模拟@Mocked("getBytes")来防止 JMockit 模拟java.lang.String类中的所有内容(想象一下这会导致什么)。

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.

现在,这个测试真的没有必要,因为“UTF-8”是一个标准字符集,要求所有 JRE 都支持。因此,在生产环境中,永远不会执行 catch 块。

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^)

不过,覆盖 catch 块的“需要”或愿望仍然有效。那么,如何在不降低覆盖率的情况下摆脱测试呢?这是我的想法:assert false;在 catch 块中插入一行作为第一条语句,并让代码覆盖率工具在报告覆盖率度量时忽略整个 catch 块。这是我的 JMockit Coverage 的“待办事项”之一。8^)

回答by Steve Freeman

How about just creating a Stringwith a bad encoding name? See

只创建一个String带有错误编码名称的怎么样?看

public String(byte bytes[], int offset, int length, String charsetName)

Mocking Stringis almost certainly a bad idea.

嘲笑String几乎肯定是一个坏主意。

回答by Jason

As others have indicated, you can't use Mockito to mock a final class. However, the more important point is that the test isn't especially useful because it's just demonstrating that String.getBytes()can throw an exception, which it can obviously do. If you feel strongly about testing this functionality, I guess you could add a parameter for the encoding to f()and send a bad value into the test.

正如其他人所指出的,您不能使用 Mockito 来模拟 final 类。然而,更重要的一点是,该测试并不是特别有用,因为它只是证明String.getBytes()可以抛出异常,这显然可以做到。如果您强烈希望测试此功能,我想您可以为编码添加一个参数f()并将错误值发送到测试中。

Also, you are causing the same problem for the caller of A.f()because Ais final and f()is static.

此外,您对调用者造成了同样的问题,A.f()因为A是最终的并且f()是静态的。

This article might be useful in convincing your coworkers to be less dogmatic about 100% code coverage: How to fail with 100% test coverage.

这篇文章可能有助于说服您的同事对 100% 代码覆盖率不那么教条:如何以 100% 测试覆盖率失败

回答by Johan

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.

您还可以使用 PowerMock 的 Mockito 扩展来模拟最终类/方法,即使是在系统类(如 String)中。但是,我也建议不要在这种情况下模拟 getBytes,而是尝试设置您的期望,以便使用填充了预期数据的真实字符串代替。

回答by Thomas L?tzer

You will be testing code that can never be executed. UTF-8 support is required to be in every Java VM, see http://java.sun.com/javase/6/docs/api/java/nio/charset/Charset.html

您将测试永远无法执行的代码。每个 Java VM 都需要 UTF-8 支持,请参阅http://java.sun.com/javase/6/docs/api/java/nio/charset/Charset.html

回答by Martin Hilton

If all you are going to do in your catch block is throw a runtime exception then you can save yourself some typing by just using a Charset object to specify your character set name.

如果您在 catch 块中要做的只是抛出一个运行时异常,那么您可以通过使用 Charset 对象来指定您的字符集名称来节省一些输入。

public final class A{
    public static String f(String str){
        return new String(str.getBytes(Charset.forName("UTF-8")));
    }
}

This way you aren't catching an exception that will never happen just because the compiler tells you to.

这样你就不会因为编译器告诉你而捕获永远不会发生的异常。