GVKun编程网logo

Java 如何在Mockito中正确匹配varargs(mockito参数匹配)

9

如果您想了解Java如何在Mockito中正确匹配varargs的相关知识,那么本文是一篇不可错过的文章,我们将对mockito参数匹配进行全面详尽的解释,并且为您提供关于JavaMockito匹配器

如果您想了解Java 如何在Mockito中正确匹配varargs的相关知识,那么本文是一篇不可错过的文章,我们将对mockito参数匹配进行全面详尽的解释,并且为您提供关于Java Mockito匹配器如何工作?、java – Gradle导入Powermock和Mockito、java – Liferay Junit-Mockito测试、java – 使用Mockito,如何与地图的键值对匹配?的有价值的信息。

本文目录一览:

Java 如何在Mockito中正确匹配varargs(mockito参数匹配)

Java 如何在Mockito中正确匹配varargs(mockito参数匹配)

我一直在尝试使用Mockito模拟具有vararg参数的方法:

interface A {  B b(int x, int y, C... c);}A a = mock(A.class);B b = mock(B.class);when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);assertEquals(b, a.b(1, 2));

这不起作用,但是如果我改为这样做:

when(a.b(anyInt(), anyInt())).thenReturn(b);assertEquals(b, a.b(1, 2));

尽管我在对方法进行存根时已经完全省略了varargs参数,但这仍然有效。

有什么线索吗?

答案1

小编典典

Mockito 1.8.1引入了anyVararg()匹配器:

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

另请参阅历史记录:https : //code.google.com/archive/p/mockito/issues/62

弃用后编辑新语法:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);

Java Mockito匹配器如何工作?

Java Mockito匹配器如何工作?

争论的Mockito匹配器(如anyargThateqsame,和ArgumentCaptor.capture())从Hamcrest匹配器表现非常不同。

  • Mockito匹配器经常导致InvalidUseOfMatchersException,即使在使用任何匹配器很长时间后执行的代码中也是如此。

  • Mockito匹配器遵循怪异的规则,例如,如果给定方法中的一个参数使用匹配器,则仅要求对所有参数使用Mockito匹配器。

  • 当覆盖Answers或使用(Integer) any()etc时,Mockito匹配器可能导致NullPointerException

  • 使用Mockito匹配器以某些方式重构代码会产生异常和意外行为,并且可能会完全失败。

为什么Mockito匹配器是这样设计的,如何实现?

答案1

小编典典

匹配器的Mockito是静态的方法和这些方法,要求站在了争论在通话过程whenverify

Hamcrest匹配器(存档版本)(或Hamcrest样式的匹配器)是无状态的通用对象实例,该实例实现Matcher<T>并公开一个方法matches(T),如果对象符合Matcher的条件,则该方法返回true。它们无副作用,通常用于断言之类的声明中。

/* Mockito */  verify(foo).setPowerLevel(gt(9000));/* Hamcrest */ assertThat(foo.getPowerLevel(), is(greaterThan(9000)));

存在Mockito匹配器,与Hamcrest风格的匹配器分开,以便匹配表达式的描述直接适合方法调用:Mockito匹配器返回THamcrest匹配器方法返回Matcher对象(类型Matcher<T>)的地方。

匹配器的Mockito通过静态方法,如调用eq,any,gt,和startsWith上org.mockito.Matchersorg.mockito.AdditionalMatchers。还有一些适配器,它们在Mockito版本之间进行了更改:

  • 对于Mockito 1.x,Matchers某些调用(例如intThat或argThat)是Mockito匹配器,它们直接接受Hamcrest匹配器作为参数。ArgumentMatcher<T>extended org.hamcrest.Matcher<T>,用于内部Hamcrest表示形式,并且是Hamcrest匹配器的基类,而不是任何Mockito匹配器。
  • 对于Mockito 2.0 +,Mockito不再直接依赖Hamcrest。Matchers调用短语为intThat或argThat包装ArgumentMatcher<T>不再实现org.hamcrest.Matcher<T>但以相似方式使用的对象。Hamcrest适配器(例如argThat和)intThat仍然可用,但已MockitoHamcrest改为使用。
    无论匹配器是Hamcrest还是简单的Hamcrest风格,都可以像这样进行修改:
/* Mockito matcher intThat adapting Hamcrest-style matcher is(greaterThan(...)) */verify(foo).setPowerLevel(intThat(is(greaterThan(9000))));

在上面的语句中:foo.setPowerLevel是一个接受的方法int。is(greaterThan(9000))返回一个Matcher<Integer>,它不能作为setPowerLevel参数。Mockito匹配器intThat包装了Hamcrest风格的Matcher并返回一个,int因此它可以作为参数出现;像gt(9000)示例代码的第一行一样,类似Mockito的匹配器会将整个表达式包装到一个调用中。

匹配者做什么/返回

when(foo.quux(3, 5)).thenReturn(true);

当不使用参数匹配器时,Mockito会记录你的参数值,并将其与equals方法进行比较。

when(foo.quux(eq(3), eq(5))).thenReturn(true);    // same as abovewhen(foo.quux(anyInt(), gt(5))).thenReturn(true); // this one''s different

当你将匹配器称为any或gt(大于)时,Mockito将存储一个匹配器对象,该对象使Mockito跳过该相等性检查并应用你选择的匹配项。在这种情况下,argumentCaptor.capture()将存储一个匹配器,该匹配器将保存其参数,以供以后检查。

匹配器返回虚拟值,例如零,空集合或null。尝试的Mockito返回一个安全,适当的虚值,如0 anyInt()any(Integer.class)或空List<String>anyListOf(String.class)。但是,由于类型擦除,Mockito缺少用于返回任何值的类型信息,但是nullfor any()argThat(...),如果尝试“自动拆箱” null原始值,则可能导致NullPointerException

匹配器喜欢eqgt获取参数值;理想情况下,应在存根/验证开始之前计算这些值。在模拟另一个呼叫的中间调用模拟可能会干扰存根。

匹配器方法不能用作返回值;例如,无法使用短语thenReturn(anyInt())或thenReturn(any(Foo.class))MockitoMockito需要确切地知道在存根调用中返回哪个实例,并且不会为你选择任意返回值。

实施细节

匹配器(作为Hamcrest样式的对象匹配器)存储在包含在名为ArgumentMatcherStorage的类中的堆栈中。MockitoCoreMatchers各自拥有一个ThreadSafeMockingProgress实例,该实例静态包含一个包含MockingProgress实例的ThreadLocal。正是这个MockingProgressImpl持有一个具体的ArgumentMatcherStorageImpl。因此,模拟和匹配器状态是静态的,但是Mockito和Matchers类之间的线程作用域一致。

最匹配的呼叫只会增加这个堆栈,与喜欢的匹配例外and,or和not。这完全对应于(并依赖于)Java的评估顺序,该顺序在调用方法之前从左到右评估参数:

when(foo.quux(anyInt(), and(gt(10), lt(20)))).thenReturn(true);[6]      [5]  [1]       [4] [2]     [3]

这将:

  1. 添加anyInt()到堆栈。
  2. 添加gt(10)到堆栈。
  3. 添加lt(20)到堆栈。
  4. 删除gt(10)lt(20)添加and(gt(10), lt(20))。
  5. 呼叫foo.quux(0, 0)(除非另有说明),它将返回默认值false。内部Mockito标记quux(int, int)为最近通话。
  6. Call when(false),它放弃其参数并准备对quux(int, int)5中标识的方法进行存根。只有两个有效状态是堆栈长度为0(等于)或2(匹配器),并且堆栈上有两个匹配器(步骤1和4),因此Mockito将方法的any()第一个参数和and(gt(10), lt(20))第二个参数与匹配器进行存根,并清除堆栈。

这演示了一些规则:

  • 可以的Mockito不能告诉之间的区别quux(anyInt(), 0)和quux(0, anyInt())。它们看起来都像是quux(0, 0)在堆栈上调用一个int匹配器。因此,如果使用一个匹配器,则必须匹配所有参数。

  • 呼叫顺序不仅很重要,而且还可以使所有工作正常进行。将匹配器提取到变量通常不起作用,因为它通常会更改调用顺序。但是,将匹配器提取为方法非常有用。

int between10And20 = and(gt(10), lt(20));/* BAD */ when(foo.quux(anyInt(), between10And20)).thenReturn(true);// Mockito sees the stack as the opposite: and(gt(10), lt(20)), anyInt().public static int anyIntBetween10And20() { return and(gt(10), lt(20)); }/* OK */  when(foo.quux(anyInt(), anyIntBetween10And20())).thenReturn(true);// The helper method calls the matcher methods in the right order.
  • 堆栈经常变化,以至于Mockito不能非常谨慎地管理它。当你与Mockito或模拟游戏进行交互时,它只能检查堆栈,并且必须接受匹配器而不知道它们是立即使用还是被意外丢弃。从理论上讲,在调用when或之外,堆栈应始终为空verify,但Mockito不能自动检查。你可以使用手动检查Mockito.validateMockitoUsage()。

  • 在对的调用中when,Mockito实际上会调用有问题的方法,如果你对方法进行了更改以引发异常(或要求非零或非空值),则该方法将引发异常。 doReturn和doAnswer(etc)不会调用实际方法,通常是一个有用的选择。

  • 如果你在存根过程中调用了模拟方法(例如,为eq匹配器计算答案),则Mockito会根据该调用检查堆栈长度,并且可能会失败。

  • 如果你尝试做一些不好的事情,例如对最终方法进行存根/验证,则Mockito将调用real方法,并在堆栈上保留额外的匹配器。该final方法调用可能不会抛出异常,但你可能会得到一个InvalidUseOfMatchersException从流浪的匹配,当你下一次交互使用模拟。

常见问题

InvalidUseOfMatchersException:

  • 如果你完全使用匹配器,并且没有在whenor verify调用之外使用匹配器,请检查每个参数是否恰好有一个匹配器调用。绝不能将匹配器用作存根返回值或字段/变量。

  • 检查是否没有在提供匹配器参数的过程中调用模拟程序。

  • 检查你是否没有尝试使用匹配器对最终方法进行存根/验证。这是将匹配器保留在堆栈中的一种好方法,除非你的final方法抛出异常,否则这可能是你唯一一次意识到所模拟的方法是final方法。

  • 具有原始参数的NullPointerException: (Integer) any()any(Integer.class)返回0时返回null ;在返回0时返回null 。NullPointerException如果你期望int使用Integer而不是Integer,则会导致此错误。在任何情况下,preferred anyInt()都将返回零,并跳过自动装箱步骤。

  • NullPointerException或其他异常:对的调用when(foo.bar(any())).thenReturn(baz)实际上会调用 foo.bar(null),你可能已经被桩打败,以在收到null参数时引发异常。切换为doReturn(baz).when(foo).bar(any())跳过存根行为。

常规故障排除

  • 使用MockitoJUnitRunner,或显式调用validateMockitoUsage你的tearDown或@After方法(运行程序将自动为你执行此操作)。这将有助于确定你是否滥用了匹配器。

  • 为了进行调试,validateMockitoUsage请直接在代码中添加对的调用。如果堆栈中有任何东西,则会抛出该错误,这是对不良症状的一个很好的警告。

java – Gradle导入Powermock和Mockito

java – Gradle导入Powermock和Mockito

感觉就像我尝试了一切.每次我使导入工作没有任何编译器错误,都会遗漏一些类.我导入了所以我除了powermockrunner.class之外还有其他东西
并且我不知道如果没有它我将如何使用powerMock

这是我的gradle文件,因为它现在看起来像

apply plugin: 'android'                                                                               

dependencies {                                                                                        
    compile filetree(dir: 'libs', include: '*.jar')                                                   
    compile project(':google-play-services_lib')                                                      
    compile project(':facebook-sdk_lib')                                                              
    compile 'de.greenrobot:greendao:1.3.7'                                                            

    compile ('com.google.dexmaker:dexmaker:1.1')                                                      
    compile ('com.google.dexmaker:dexmaker-mockito:1.1'){                                             
        exclude group: 'org.hamcrest'                                                                 
        exclude group: 'org.javassist'                                                                
    }                                                                                                 

    compile('junit:junit:4.4'){                                                                       
       exclude group: 'org.hamcrest'                                                                  
    }                                                                                                 

    compile('org.mockito:mockito-core:1.9.5'){                                                        
        exclude group: 'org.hamcrest'                                                                 
    }                                                                                                 

    compile('org.powermock:powermock-api-mockito:1.4.12'){                                            
       exclude group: 'org.hamcrest'                                                                  
    }                                                                                                                                                                                                                                                                                                

}                                                                                                     

android {                                                                                             

    packagingOptions {                                                                                
        exclude 'meta-inf/ASL2.0'                                                                     
        exclude 'meta-inf/LICENSE'                                                                    
        exclude 'meta-inf/LICENSE.txt'                                                                
        exclude 'meta-inf/NOTICE'                                                                     
        exclude 'meta-inf/NOTICE.txt'                                                                 


    }                                                                                                 

    compileSdkVersion 19                                                                              
    buildToolsversion "19.1.0"                                                                        

    lintOptions {                                                                                     
        abortOnError false                                                                            
    }                                                                                                 

    sourceSets {                                                                                      
        main {                                                                                        
            manifest.srcFile 'AndroidManifest.xml'                                                    
            java {                                                                                    
                srcDir 'src'                                                                          
                srcDir 'src-gen'                                                                      
                srcDir 'tests'                                                                        
            }                                                                                         
            resources {                                                                               
                srcDir 'src'                                                                          
                srcDir 'src-gen'                                                                      
            }                                                                                         
            aidl.srcDirs = ['src','src-gen']                                                          
            renderscript.srcDirs = ['src','src-gen']                                                  
            res.srcDirs = ['res']                                                                     
            assets.srcDirs = ['assets']                                                               
        }                                                                                             
        androidTest.setRoot('tests')                                                                  

        debug.setRoot('build-types/debug')                                                            
        release.setRoot('build-types/release')                                                        
    }                                                                                                 
}  

这是编译器错误,我整天都被扔进了我的脸.

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Lorg/hamcrest/Description;
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:594)
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:552)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:533)
at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:170)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:188)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:439)
at com.android.dx.command.dexer.Main.runMonoDex(Main.java:287)
at com.android.dx.command.dexer.Main.run(Main.java:230)
at com.android.dx.command.dexer.Main.main(Main.java:199)
at com.android.dx.command.Main.main(Main.java:103)

解决方法:

我昨天整天坐着弄清楚我应该如何解决这个问题.它现在已经解决了,这就是我用gradle.build文件解决问题的方法

apply plugin: 'android'                                                                              

repositories {                                                                                       
    mavenCentral()                                                                                   
}                                                                                                    

dependencies {                                                                                       
    compile filetree(dir: 'libs', include: '*.jar')                                                  
    compile project(':google-play-services_lib')                                                     
    compile project(':facebook-sdk_lib')                                                             


    compile ('de.greenrobot:greendao:1.3.7'){                                                        
       exclude group: 'org.hamcrest'                                                                 
    }                                                                                                

    androidTestCompile('org.mockito:mockito-core:1.9.5'){                                            
        exclude group: 'org.hamcrest'                                                                
    }                                                                                                

    androidTestCompile('org.powermock:powermock-module-junit4:1.5'){                                 
        exclude group: 'org.hamcrest'                                                                
    }                                                                                                
}                                                                                                    

android {                                                                                            

    packagingOptions {                                                                               
        exclude 'meta-inf/ASL2.0'                                                                    
        exclude 'meta-inf/LICENSE'                                                                   
        exclude 'meta-inf/LICENSE.txt'                                                               
        exclude 'meta-inf/NOTICE'                                                                    
        exclude 'meta-inf/NOTICE.txt'                                                                
    }                                                                                                

    compileSdkVersion 19                                                                             
    buildToolsversion "19.1.0"                                                                       

    lintOptions {                                                                                    
        abortOnError false                                                                           
    }                                                                                                

    sourceSets {                                                                                     
        main                                                                                         
        {                                                                                            
            manifest.srcFile 'AndroidManifest.xml'                                                   
            resources                                                                                
            {                                                                                        
                srcDir 'src-gen'                                                                     
            }                                                                                        
            aidl.srcDirs = ['src/main/java', 'src-gen']                                              
            renderscript.srcDirs = ['src/main/java', 'src-gen']                                      
            res.srcDirs = ['res']                                                                    
            assets.srcDirs = ['assets']                                                              
            java.srcDirs = ['src/main/java', 'src/main/java/', 'src-gen']                            
        }                                                                                            

        androidTest {                                                                                
            java.srcDirs = ['tests']                                                                 
        }                                                                                            


        unitTest {                                                                                   
            java.srcDir file('tests')                                                                
            resources.srcDir file('tests/res')                                                       
        }                                                                                            


        debug.setRoot('build-types/debug')                                                           
        release.setRoot('build-types/release')                                                       
    }//-End of sourceSet                                                                             
}  //-End of android    

希望这可以帮助至少一个人不会陷入我通过的同一个gradle故障循环中.

java – Liferay Junit-Mockito测试

java – Liferay Junit-Mockito测试

我正在尝试使用JUNIT和Mockito测试我的liferay portlet插件代码.目前,我正在模拟服务实现以返回模拟数据并测试功能.

我面临的问题是,我需要测试一些带有属性的代码:
PropsUtil.get( “someKey”)
但是当我将它作为独立的JUNIT测试运行时,PropsUtil不会从任何属性文件中读取.
有没有什么办法可以在不更改源代码的情况下从liferay属性(portal * .properties)文件中读取测试?

解决方法

我使用以下方法:

>我的TestClass扩展了BaseServiceTestCase(在liferay src中可用)
>将portal-test.properties保留在test文件夹中(带有测试值).
>运行测试用例.

在这种情况下,liferay会加载所有属性以及弹簧初始化.

java – 使用Mockito,如何与地图的键值对匹配?

java – 使用Mockito,如何与地图的键值对匹配?

我需要根据一个特定的键值从一个模拟对象发送一个特定的值.

具体类:

map.put("xpath","PRICE");
search(map);

从测试用例:

IoUrXMLDocument mock = mock(IoUrXMLDocument.class);
when(mock.search(.....need help here).thenReturn("$100.00");

我如何模拟这个方法调用这个键值对?

解决方法

我发现这试图解决一个类似的问题,创建一个带有Map参数的Mockito存根.我不想为有关的地图编写一个自定义匹配器,然后我找到一个更优雅的解决方案:在mockito的argThat中使用 hamcrest-library中的附加匹配器:
when(mock.search(argThat(hasEntry("xpath","PRICE"))).thenReturn("$100.00");

如果您需要检查多个条目,那么您可以使用其他hamcrest的好东西:

when(mock.search(argThat(allOf(hasEntry("xpath","PRICE"),hasEntry("otherKey","otherValue")))).thenReturn("$100.00");

这开始长时间不平凡的地图,所以我最终提取方法来收集入门匹配器并将它们粘贴到我们的TestUtils中:

public static <K,V> Matcher<Map<K,V>> matchesEntriesIn(Map<K,V> map) {
    return allOf(buildMatcherArray(map));
}

public static <K,V>> matchesAnyEntryIn(Map<K,V> map) {
    return anyOf(buildMatcherArray(map));
}

@SuppressWarnings("unchecked")
private static <K,V> Matcher<Map<? extends K,? extends V>>[] buildMatcherArray(Map<K,V> map) {
    List<Matcher<Map<? extends K,? extends V>>> entries = new ArrayList<Matcher<Map<? extends K,? extends V>>>();
    for (K key : map.keySet()) {
        entries.add(hasEntry(key,map.get(key)));
    }
    return entries.toArray(new Matcher[entries.size()]);
}

所以我留下来:

when(mock.search(argThat(matchesEntriesIn(map))).thenReturn("$100.00");
when(mock.search(argThat(matchesAnyEntryIn(map))).thenReturn("$100.00");

有一些与泛型相关的丑陋,我正在抑制一个警告,但至少它是干燥的,隐藏在TestUtil中.

最后一个注意事项,请注意embedded hamcrest issues in JUnit 4.10.使用Maven,我建议先导入hamcrest-library,然后再导入JUnit 4.11(并且从JUnit中排除hamcrest-core只是为了很好的测量):

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-library</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

今天的关于Java 如何在Mockito中正确匹配varargsmockito参数匹配的分享已经结束,谢谢您的关注,如果想了解更多关于Java Mockito匹配器如何工作?、java – Gradle导入Powermock和Mockito、java – Liferay Junit-Mockito测试、java – 使用Mockito,如何与地图的键值对匹配?的相关知识,请在本站进行查询。

本文标签: