Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Junit Jupiter:如何更改缓存在测试类外部列表中的mockito模拟行为?_Java_Unit Testing_Arraylist_Mockito_Junit5 - Fatal编程技术网

Java Junit Jupiter:如何更改缓存在测试类外部列表中的mockito模拟行为?

Java Junit Jupiter:如何更改缓存在测试类外部列表中的mockito模拟行为?,java,unit-testing,arraylist,mockito,junit5,Java,Unit Testing,Arraylist,Mockito,Junit5,在我的场景中,我有一个简单的类,其方法返回字符串: public class Foo { public String bar() { return "1"; } } 对于简化,列表将存储Foo的实例(在现实项目中,这是某种工厂/缓存组合): 第一个测试方法firstTest成功完成,foo返回“2”,并且foo现在存储在缓存列表中。 第二种测试方法secondTest因“2不等于3”而失败,因为 将更改foo的行为,但对缓存中的模拟对象没有影

在我的场景中,我有一个简单的类,其方法返回字符串:

public class Foo {
    public String bar() {
        return "1";
    }
}
对于简化
列表将存储
Foo
的实例(在现实项目中,这是某种工厂/缓存组合):

第一个测试方法
firstTest
成功完成,
foo
返回“2”,并且
foo
现在存储在缓存列表中。 第二种测试方法
secondTest
因“2不等于3”而失败,因为

将更改
foo
的行为,但对缓存中的模拟对象没有影响,该对象将用于调用FooCache#getOrCreateFoo


为什么会出现这种情况?在将模拟对象存储在测试类之外的列表中之后,有没有任何方法可以更改模拟对象的行为?

有多种方法可以解决此问题

  • 重构静态类并包含
    clearCache
    方法
  • 公共类缓存{
    私有静态列表缓存=新的ArrayList();
    公共静态Foo getOrCreateFoo(Foo-Foo){
    if(cache.isEmpty()){
    cache.add(foo);
    }
    返回cache.get(0);
    }
    公共静态void clearFooCache(){
    cache.clear();
    }
    }
    
    在你的测试中

    @BeforeEach
    public void setUp() {
        FooCache.clearCache();
    }
    
    
  • 使用反射访问
    FooCache\cache
  • @beforeach
    公共作废设置(){
    字段缓存=FooCache.class.getDeclaredField(“缓存”);
    cache.setAccessible(true);
    List listofoos=(List)cache.get(FooCache.class);
    listofoos.clear();
    }
    
  • 在每个测试中使用Mockito的
    mockStatic
    实用程序
  • try(MockedStatic theMock=Mockito.mockStatic(YearMonth.class,Mockito.CALLS\u REAL\u方法)){
    doReturn(anyValue).when(theMock.getOrCreateFoo(any());
    }
    
    仅解释一下这里发生了什么:

  • 在firstTest()启动之前,将创建一个新的Foo mock,稍后将返回“2”。这将添加到静态缓存中。断言是正确的

  • 在启动secondTest()之前,将创建另一个新的Foo mock,稍后将返回“3”。这将添加到静态缓存中。由于代码是静态的,所以仍然包含第一个mock,这使得断言失败

  • 经验教训:

    静态代码是有害的,尤其是静态非常量类属性。即使工厂也应该以非静态的方式创建/使用。这是一种反模式

    解决方案:

  • 从代码中删除所有静态修饰符

  • 在每次测试运行时实例化FooCache:

  • public-class-FooTest{
    @嘲弄
    私人富福;
    //测试中的系统(将在每次测试前实例化)
    //这就是您实际测试的对象。
    private FooCache sut=new FooCache();
    @试验
    void firstTest(){
    //安排
    when(foo.bar())。然后返回(“2”);
    //表演
    Foo uut=sut.getOrCreateFoo(Foo);
    字符串实际值=uut.bar();
    //断言
    资产质量(“2”,实际);
    }
    @试验
    void secondTest(){
    //安排
    when(foo.bar())。然后返回(“3”);
    //表演
    Foo uut=sut.getOrCreateFoo(Foo);
    字符串实际值=uut.bar();
    //断言
    assertEquals(“3”,实际);//失败,3不等于2
    }
    }
    
    刚刚找到了原因:正如中及其第一和第二条注释所述,“JUnit设计者希望测试方法之间的测试隔离,因此它创建了一个新的测试类实例来运行每个测试方法。”

    因此,来自第一个方法的foo存储在列表中,随着第二个测试方法的开始,另一个foo已经创建!以下任何更改将不再影响第一个方法foo


    这不仅是我的szenario中的一个问题,而且在处理单例时

    可以在setUp()方法上添加@beforeach注释,在这里可以实际为每个测试创建新的模拟,使用foo=Mockito.mock(foo.class)重新初始化;或者用Mockito.reset(foo)重置;不会解决此问题,因为无法访问列表中的foo对象。谢谢,我知道这些解决此问题的选项,但我对发生这种情况的原因更感兴趣。请参阅我的答案,了解发生了什么以及为什么保留静态代码不是解决方案。
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.mockito.Mockito.when;
    
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;
    
    @ExtendWith(MockitoExtension.class)
    public class FooTest {
    
        @Mock
        private Foo foo;
    
        @Test
        void firstTest() {
            // Arrange
            when(foo.bar()).thenReturn("2");
            // Act
            Foo uut = FooCache.getOrCreateFoo(foo);
            String actual = uut.bar();
            // Assert
            assertEquals("2", actual);
        }
    
        @Test
        void secondTest() {
            // Arrange
            when(foo.bar()).thenReturn("3"); // <--- HAS NO EFFECT ON CACHED FOO
            // Act
            Foo uut = FooCache.getOrCreateFoo(foo);
            String actual = uut.bar();
            // Assert
            assertEquals("3", actual); // fails with 3 not equals 2
        }
    }
    
    when(foo.bar()).thenReturn("3") 
    
    @BeforeEach
    public void setUp() {
        FooCache.clearCache();
    }