Mokito/Java - 静态方法模拟

Mokito/Java - Static Methods Mock

例如我有以下 classes:

public class TesteEstatico {

   public static String teste(){
      return "FOO";
   }

}

我有一个 class 使用了她的方法:

public class UsaTesteEstatico {

   public String metodoParaTeste1 (){
       return  TesteEstatico.teste() + " BAR ";
   }

   public String metodoParaTeste2 (){
       return  "FOO "+TesteEstatico.teste() + " BAR ";
   }

}

测试class:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; 
import org.mockito.InjectMocks; 
import org.mockito.Mockito; 
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class) public class UsaTesteEstaticoTest {

   @InjectMocks
   UsaTesteEstatico usaTesteEstatico;

   @Test
   void teste1(){
       Mockito.mockStatic(TesteEstatico.class);
       Mockito.when(TesteEstatico.teste())
            .thenReturn("BANANA");

       String res = usaTesteEstatico.metodoParaTeste1();
       System.out.println(res);
   }

   @Test
   void teste2(){
       Mockito.mockStatic(TesteEstatico.class);
       Mockito.when(TesteEstatico.teste())
            .thenReturn("LARANJA");

       String res = usaTesteEstatico.metodoParaTeste2();
       System.out.println(res);

   }
}

尝试 运行 测试时出现错误:

org.mockito.exceptions.base.MockitoException: 对于 TesteEstatico,静态模拟已经在当前线程中注册 要创建新的 mock,必须注销现有的静态 mock 注册

项目中的库版本:

知道如何解决这个问题,我尝试了一些方法但没有成功。

注意:我无法更改或添加任何新库,因为它是一个受限项目。

你需要使用静态块来模拟它。

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;


@ExtendWith(MockitoExtension.class)
public class UsaTesteEstaticoTest {
    @InjectMocks
    UsaTesteEstatico usaTesteEstatico;

    @BeforeAll
    public static void init(){
        Mockito.mockStatic(TesteEstatico.class);
    }

    @Test
    void teste1(){

        Mockito.when(TesteEstatico.teste())
                .thenReturn("BANANA");

        String res = usaTesteEstatico.metodoParaTeste1();
        System.out.println(res);
    }

    @Test
    void teste2(){

        Mockito.when(TesteEstatico.teste())
                .thenReturn("LARANJA");

        String res = usaTesteEstatico.metodoParaTeste2();
        System.out.println(res);

    }
}

您应该在每个测试中使用 try-with-resources 块来关闭 mockStatic。

public class UsaTesteEstaticoTest {
    
    UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();

    @Test
    void teste1(){
        try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
            Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
            String res = usaTesteEstatico.metodoParaTeste1();
            System.out.println(res);
        }
    }

    @Test
    void teste2(){
        try (var ms = Mockito.mockStatic(TesteEstatico.class)) {
            Mockito.when(TesteEstatico.teste()).thenReturn("LARANJA");
            String res = usaTesteEstatico.metodoParaTeste2();
            System.out.println(res);
        }
    }
}

注意@BeforeAll 中的 mockStatic

使用@BeforeAll是一个陷阱和糟糕的建议。 你应该争取互不影响的独立测试。 @BeforeAll 中调用的 mockStatic 不是这种情况,因为测试方法的存根比测试方法长。

例如

// BAD CODE DONT USE
public class UsaTesteEstaticoTest {

    UsaTesteEstatico usaTesteEstatico = new UsaTesteEstatico();
    static MockedStatic<TesteEstatico> ms;

    @BeforeAll
    public static void init() {
        ms = Mockito.mockStatic(TesteEstatico.class);
    }

    @AfterAll
    public static void close() {
        ms.close();
    }


    @Test
    void teste1() {
        Mockito.when(TesteEstatico.teste()).thenReturn("BANANA");
        String res = usaTesteEstatico.metodoParaTeste1();
        System.out.println(res);
    }

    @Test
    void teste2() {
        String res = usaTesteEstatico.metodoParaTeste2();
        System.out.println(res);
    }
}

teste2 打印:

  • FOO BANANA BAR 如果 运行 在 teste1
  • 之后
  • FOO null BAR 如果 运行 单独

这正是您要避免的。