【问题标题】:Can't mock java.lang.System#exit(int) method with PowerMock无法使用 PowerMock 模拟 java.lang.System#exit(int) 方法
【发布时间】:2019-01-05 20:16:59
【问题描述】:

我的应用程序有一个流程,最后调用了方法 System.exit(int)

我正在尝试通过使用 TestNG 运行测试来测试此流程。

但是,在运行测试时,尽管测试已完成,但我收到了这条奇怪的消息:

为了找到根本原因,我把System.exit(int)从实际流程中去掉了,测试按预期通过了,所以这里的问题是System.exit(int)方法。

为了解决这个问题,我尝试模拟有问题的方法,但找不到正确的方法。这就是我所做的

  1. 我在测试类的@PrepareForTest 下添加了java.lang.System.class
  2. 在测试中添加了PowerMockito.mockStatic(java.lang.System.class)
  3. 我尝试以两种方式模拟该方法:

    一个。

        PowerMockito.replace(PowerMockito.method(System.class, "exit", int.class))
        .with((proxy, method, args) -> null);
    

    当以这种方式运行时,mock 似乎不起作用,因为我在测试结束时收到了相同的消息,在 System.exit(int) 上没有应用任何 mocks 时我也收到了相同的消息

b.

PowerMockito.doNothing().when(System.class, "exit", Mockito.any());

通过这种方式,我在测试开始时遇到了这个异常:

org.powermock.reflect.exceptions.MethodNotFoundException: No method found with name 'exit' with parameter types: [ <none> ] in class java.lang.System.

我已经以这种方式模拟了一些方法,不知道为什么 System.exit(int) 不起作用。

有什么想法吗? 谢谢

【问题讨论】:

  • 我认为这根本不是一个好主意。你应该认真考虑重新设计你的系统,只在你的 main 方法中调用System.exit,而不是其他地方。
  • 我无法更改此代码,我必须找到模拟问题的解决方案
  • 经过检查,System.exit() 只在应用程序末尾的一个地方使用!不知道为什么它在调用 System.exit() 后将测试显示为“未开始”(如上图所示)。删除 System.exit() 时,测试成功通过。
  • 所以你的问题已经解决了,不是吗?
  • 一点也不。我已经编辑了这个问题。希望现在更清楚了。

标签: java testng powermock powermockito


【解决方案1】:

有趣的问题,我也不知道,但显然不使用 Powermock,通过使用 SecurityManagers 是可能的。引用自original post

今天我正在为我们的一个命令行工具编写测试,我有 这个问题的方法是倾倒一切,我真的 需要调用,因为这是我正在检查的结果,也 称为 System.exit()。无论如何,我必须找到一种方法来测试它。一世 考虑过使用 PowerMock 和模拟系统,但那会 很复杂,因为我必须找到确切的类调用 System.exit()。所以这是另一个避免 System.exit 退出(是的,这可能我不知道 要么)。

秘密在于Java的SecurityManager机制,这个类 不仅可以检查权限,还可以检查退出 事件。因此,如果您想停止 退出

以下是我在 IJ 中测试的完整示例。请注意,示例应该是故意失败的:

java.lang.AssertionError: 
Expected: is <10>
     but: was <5>
Expected :is <10>
Actual   :<5>

package com.example;

import org.junit.Test;

import java.security.Permission;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

public class SystemExitTest {

    @Test
    public void shouldExitWithSpecificCode() {
        //save the initial security manager (probably null)
        SecurityManager initialSecurityManger = System.getSecurityManager();
        try {
            // set security manager
            System.setSecurityManager(new NoExitSecurityManager());

            // execute code under test
            new MyClass().exit(5);

            // ensure this point is not reached by any chance
            fail("Should've thrown ExitException");
        } catch (ExitException e) {
            // validate exit code
            assertThat(e.status, is(10)); // <== this fails on purpose
        } finally {
            // restore initial security manager (otherwise a new ExitException will be thrown when the JVM will actually exit)
            System.setSecurityManager(initialSecurityManger);
        }
    }


    // class under test
    public static class MyClass {
        public void exit(int code) {
            System.exit(code);
        }
    }

    // exception to be thrown by security manager when System.exit is called
    public static class ExitException extends SecurityException {
        public final int status;

        public ExitException(int status) {
            this.status = status;
        }
    }


    // custom security manager
    public static class NoExitSecurityManager extends SecurityManager {
        @Override
        public void checkPermission(Permission perm) {
        }

        @Override
        public void checkPermission(Permission perm, Object context) {
        }

        @Override
        public void checkExit(int status) {
            super.checkExit(status);
            throw new ExitException(status);
        }
    }
}

【讨论】:

  • 可以肯定很多人都以此为基础进行调查。我想这使我对该主题的研究减少了一半。干杯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-19
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多