在单元测试中捕获未知的主机异常

时间:2015-11-06 07:49:37

标签: java unit-testing refactoring

我正在编写一些单元测试,并尝试从代码中覆盖尽可能多的内容。现在我想编写一个验证本地主机名称的测试。

方法如下:

public static String getLocalhostName() {
      try {
         return InetAddress.getLocalHost().getHostName();
      }
      catch ( final UnknownHostException e ) {
         throw new RuntimeException( e.getMessage() );
      }
   }

和我的测试:

@Test
public void testGetLocalhostName() {
  final String host = getLocalhostName();
  Assert.assertEquals( "mycomputer", host );
}

问题是我如何重构这个以便覆盖主方法中的catch块?

5 个答案:

答案 0 :(得分:2)

@Test
public void testGetLocalhostName() {
    String url = "http://hostname/xxxxx";
    Throwable throwable = catchThrowable(() -> foo.getLocalHost(url));

    assertThat(throwable)
        .isNotNull()
        .isInstanceOf(RuntimeException.class)
        .hasMessage("your message");
    assertThat(throwable.getCause()).isInstanceOf(UnknownHostException.class);
}

您可以通过这种方式测试异常类型,消息和测试主机名。

答案 1 :(得分:1)

根据经验,你不应该嘲笑外部库。通过嘲弄,您可以假设它的行为方式,可能不是这种情况或将来会发生什么变化。您应该只模拟您控制的代码和具有测试的代码。

在这种情况下,我会将InetAddress.getLocalHost().getHostName()提取到另一个类HostNameProvider,然后执行依赖注入,就像在John Williams'回答,但没有使用电源模拟。那么你将有两个测试当前的类和一个HostNameProvider(只有快乐的路径)。

然而,在应用测试驱动开发时,在实践中会发生什么?你编写了一个测试,你编写实现并且它没有编译,所以你必须通过添加try-catch来修复它。在这一点上,我会依赖没有开发人员如此不负责任地吞下异常。如果有人这样做,你就会进行静态分析 - IntelliJ和Sonar都会强调这一点。简而言之 - 我不会测试它。

我还建议修改模板,因此当您生成try-catch时,它会自动在catch中添加throw new RuntimeException(e);

答案 2 :(得分:0)

这将为您提供测试覆盖率。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

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

import static org.mockito.BDDMockito.given;


@RunWith(PowerMockRunner.class)
@PrepareForTest(InetAddress.class)
public class Mocker {

    @Mock
    private java.net.InetAddress mockedAddress;

    @Test(expected = RuntimeException.class)
    public void testGetLocalHostName() throws Exception {

        //given
        PowerMockito.mockStatic(InetAddress.class);
        given(InetAddress.getLocalHost()).willReturn(mockedAddress);
        given(mockedAddress.getHostName()).willThrow(UnknownHostException.class);

        //when
        getLocalhostName();
    }

    public static String getLocalhostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (final UnknownHostException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

}

但它并不令人满意,因为它不会在抛出RuntimeException之前验证预期的代码是否已运行。

捕获异常可能会更好,然后您可以验证对模拟类的调用。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

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

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;


@RunWith(PowerMockRunner.class)
@PrepareForTest(InetAddress.class)
public class Mocker {

    @Mock
    private java.net.InetAddress mockedAddress;

    @Test
    public void testGetLocalHostName() throws Exception {

        //given
        PowerMockito.mockStatic(InetAddress.class);
        given(InetAddress.getLocalHost()).willReturn(mockedAddress);
        given(mockedAddress.getHostName()).willThrow(UnknownHostException.class);

        //when
        try {
            getLocalhostName();
        } catch (RuntimeException e) {
            PowerMockito.verifyStatic();
            InetAddress.getLocalHost();
            verify(mockedAddress).getHostName();
        }
    }

    public static String getLocalhostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (final UnknownHostException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

}

答案 3 :(得分:0)

我遇到了同样的问题,并提出了以下建议:

private static String getHostName() {
    // unit test trickery!!!
    // the TRY should simply return Inet4Address.getLocalHost().getHostName().
    // but, there's no way to force an UnknownHostException, so we can't
    // get 100% coverage. 100% coverage isn't entirely necessary, but it
    // was the only uncovered line in the entire project, so let's make it happen.
    // throw the exception manually and use it to propagate the host name to the
    // catch, then the catch returns it. BOOM! Sorcery.
    // This is only called once during the app life time, so we can take
    // a hit on the unnecessary exception.
    try {
        throw new UnknownHostException(Inet4Address.getLocalHost().getHostName());
    } catch (final UnknownHostException e) {
        return e.getMessage();
    }
}

它只使用了一次(结果被分配给一个变量,并且该变量被重用),所以我愿意承担道德冲击。

当然,如果确实存在异常,则不会进行区分。可以详细说明CATCH来解决这一问题。 (即:如果结果中有空格,则不是主机名)

答案 4 :(得分:-2)

public static String getLocalhostName() throws UnknownHostException  {
      try {
         return InetAddress.getLocalHost().getHostName();
      }
      catch ( final UnknownHostException e ) {
         throw new RuntimeException( e.getMessage() );
      }
   }

@Test
public void testGetLocalhostName() throws UnknownHostException  {
  final String host = getLocalhostName();
  Assert.assertEquals( "mycomputer", host );
}

并在main中使用try..catch或声明它抛出Exception或UnKnownHostException