【问题标题】:Failing unit test with mockito.mockStatic使用 mockito.mockStatic 失败的单元测试
【发布时间】:2021-09-24 15:26:15
【问题描述】:

所以,我正在编写一个 junit 测试,但我似乎无法弄清楚它失败的原因。我正在使用 Mockito.mockStatic 来模拟 InetAddres.class。一次运行所有单元测试失败。分别运行它们成功。我知道静态块被初始化一次。我似乎无法弄清楚为什么每个单元测试都没有重新初始化类 Host 。任何帮助表示赞赏

J

这是我的代码:

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import java.net.InetAddress;
import java.net.UnknownHostException;

import static org.assertj.core.api.Assertions.assertThat;


class HostTest {

    @Test
    void testLocalhost() {
        try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
            InetAddress inetAddress = Mockito.mock(InetAddress.class);
            Mockito.when(inetAddress.getHostName()).thenReturn("LOCALHOST");
            inetAddressMockedStatic.when(InetAddress::getLocalHost).thenReturn(inetAddress);
            assertThat(Host.getLOCALHOST()).isEqualTo("LOCALHOST");
            Mockito.reset(inetAddress);

        }
    }

    @Test
    void testIP() {
        try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
            InetAddress inetAddress = Mockito.mock(InetAddress.class);
            Mockito.when(inetAddress.getHostAddress()).thenReturn("127.0.0.1");
            inetAddressMockedStatic.when(InetAddress::getLocalHost).thenReturn(inetAddress);
            assertThat(Host.getIP()).isEqualTo("127.0.0.1");
        }
    }

    @Test
    void testUnkownHostExceptionIP() {
        try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
            inetAddressMockedStatic.when(InetAddress::getLocalHost).thenThrow(UnknownHostException.class);
            assertThat(Host.getIP()).isEqualTo("Unkown ip");
        }

    }

    @Test
    void testUnkownHostExceptionLocalhost() {
        try (MockedStatic<InetAddress> inetAddressMockedStatic = Mockito.mockStatic(InetAddress.class)) {
            inetAddressMockedStatic.when(InetAddress::getLocalHost).thenThrow(UnknownHostException.class);
            assertThat(Host.getLOCALHOST()).isEqualTo("Unkown hostname");

        }
    }
}




import java.net.InetAddress;
import java.net.UnknownHostException;

public class Host {

    private static String LOCALHOST;
    private static String IP;

    static {
        try {
            InetAddress localhost = InetAddress.getLocalHost();
            LOCALHOST = localhost.getHostName();
            IP = localhost.getHostAddress();
        } catch (UnknownHostException e) {
            LOCALHOST = "Unkown hostname";
            IP = "Unkown ip";
        }
    }

    public static String getLOCALHOST() {
        return LOCALHOST;
    }

    public static String getIP() {
        return IP;
    }
}

【问题讨论】:

    标签: java unit-testing static mockito


    【解决方案1】:

    静态初始化器只执行一次,在加载类时。这意味着它只会在使用Host 类的第一个测试用例中运行。

    在您的示例中,一旦testLocalhost 运行,该类将在Host.getLOCALHOST() 行中使用,此时其初始化程序已被执行。在其余的单元测试中,它永远不会再次运行。

    如果你切换这些测试用例的顺序,你会得到不同的结果。

    根据您的测试用例判断,您可以采取一些措施来使代码符合您的期望。由于 IP 和主机名会在您的程序执行过程中发生变化,因此它们不应该是在静态初始化程序块中设置的静态成员。

    1. 摆脱共享状态。撇开并发性和内存可见性不谈,static 成员将对类的所有实例可见。省略 static 关键字并将其变为常规字段

      public class Host {
      
          private final String hostName;
      
          private final String ip;
      
          // Constructor, use this to build new instances
          public Host(String hostName, String ip) {
              this.hostName = hostName;
              this.ip = ip;
          }
      
          // No longer static, this is now an instance method
          public getHostName() {
              return this.hostName;
          }
      
          public getIp() {
              return this.ip;
          }
      }
      
    2. 构建类的实例,将参数传递给构造函数以自定义其行为。

      // Host.getIp(); // If IP and host name can vary, don't make them static
      InetAddress localhost = InetAddress.getLocalHost();
      // build a new instance of Host, provide the relevant data at construction time
      Host testedHost = new Host(localhost.getHostName(), localhost.getHostAddress());
      // call the instance method, this doesn't affect your other tests
      assertThat(testedHost.getIp()).is(someIp);
      // at this point, the Host instance you created may be garbage-collected to free memory (you don't need to do that yourself)
      

      现在每个测试用例都将独立于其他测试用例。每次需要时只需创建一个 Host 的新实例。

    3. 摆脱静态模拟。注意InetAddress 方法调用是如何移到Host 类之外的。通过将它们传递给构造函数,您可以使代码更易于测试。 Inversion of control 实现了。

    您可以使用工厂方法来代替公共构造函数。底线是,如果您想让类改变其行为,通常最好创建新实例并封装任何状态。

    静态类和成员更适合诸如在程序执行期间不会改变的不可变常量,或不依赖任何内部状态的实用方法,即pure functions

    【讨论】:

      猜你喜欢
      • 2012-06-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-13
      • 1970-01-01
      相关资源
      最近更新 更多