在junit测试中模拟一个DateFormat类

时间:2017-02-06 12:49:45

标签: java unit-testing junit mockito

我正在尝试模拟一个DateFormat类,因为它在我的单元测试范围内没有任何意义。我正在使用org.mockito.Mockito库。

以下代码:

import static org.mockito.Mockito.when;
import static org.mockito.Mockito.any;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import org.junit.Before;

public class someTest {

    @Mock
    DateFormat formatter; 

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
        when(formatter.format(any(Date.class))).thenReturn("2017-02-06");
    }
}

给出以下错误:

  

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:   参数匹配器的使用无效! 3匹配预期,1记录:

     

- >在someTest.before(someTest.java:33)

     

如果匹配器与原始值组合,则可能会发生此异常:       //不正确:       someMethod(anyObject(),“raw String”);使用匹配器时,所有参数都必须由匹配器提供。例如:       //正确:       someMethod(anyObject(),eq(“by matcher”));

     

有关更多信息,请参阅Matchers类的javadoc。

     

at java.text.DateFormat.format(Unknown Source)
  在   someTest.before(someTest.java:33)

如何以正确的方式模拟DateFormat类?

4 个答案:

答案 0 :(得分:3)

问题在于format(Date date)

的实施
public final String format(Date date) {
    return format(date, new StringBuffer(),
                  DontCareFieldPosition.INSTANCE).toString();
}

如你所见,这是最终的。 Mockito无法模拟最终方法。相反,它会调用真正的方法。 作为解决方法,您可以模拟方法format(date, new StringBuffer(), DontCareFieldPosition.INSTANCE)

when(formatter.format(any(Date.class), any(StringBuffer.class), 
                      any(FieldPosition.class)))
    .thenReturn(new StringBuffer("2017-02-06"));

因此,当方法format(date)将调用您的模拟方法时,结果将如您所愿。

答案 1 :(得分:3)

正如Serghey Bishyr指出的那样,你试图嘲笑final method,这在Mockito无法做到。

如果你的模拟框架不允许你做某事(比如模拟最终方法),你要么必须找到一个替代框架(比如Powermock),要么以另一种方式解决它。

来自Wikipedia article about mocks

  

在单元测试中,模拟对象可以模拟复杂的真实对象的行为,因此当真实对象不可行或不可能合并到单元测试中时非常有用。如果对象具有以下任何特征,则在其位置使用模拟对象可能很有用:

     
      
  • 对象提供非确定性结果(例如当前时间或当前温度);
  •   
  • 它具有难以创建或复制的状态(例如网络错误);
  •   
  • 它很慢(例如一个完整的数据库,必须在测试之前进行初始化);
  •   
  • 它尚不存在或可能改变行为;
  •   
  • 它必须包含专门用于测试目的的信息和方法(而不是其实际任务)。
  •   

以上几点均不适用于您的代码,因此无需使用模拟。使用DateFormat的实际实现并不“不切实际或不可能”。

不提供模拟的DateFormat,而是提供SimpleDateFormat

formatter = new SimpleDateFormat("'2017-02-06'");

对于任何输入,这将始终返回2017-02-06,这显然是问题代码所需要的,因为'会导致它们之间的文字被字面意思理解。

答案 2 :(得分:0)

除了正确答案之外,还有一个重要的注意事项:

when(formatter.format(any(Date.class))

如果方法不是最终的,你可以选择

when(formatter.format(any())

Mockito足够聪明,能够理解发生了什么以及发生了什么(至少在使用Java8时)

答案 3 :(得分:0)

您可以使用PowerMock来做到这一点。

在您的app.gradle中添加此依赖项

testImplementation "org.powermock:powermock-module-junit4:${versions.powermock}"
testImplementation "org.powermock:powermock-module-junit4-rule:${versions.powermock}"
testImplementation "org.powermock:powermock-api-mockito2:${versions.powermock}"
testImplementation "org.powermock:powermock-classloading-xstream:${versions.powermock}"

然后

@RunWith(PowerMockRunner::class)
@PrepareForTest(android.text.format.DateFormat::class)
class YourTestClass {

@Before
fun setup() {
    PowerMockito.mockStatic(android.text.format.DateFormat::class.java)
    val format = SimpleDateFormat()
    format.applyPattern("dd/MM/y") //your format here
    PowerMockito.`when`(android.text.format.DateFormat.getDateFormat(any(Context::class.java))).thenAnswer {
        format
    }
}

... tests