【问题标题】:Testing main method by junit [duplicate]通过junit测试主要方法[重复]
【发布时间】:2016-07-20 21:02:06
【问题描述】:

我写了一个类,它接受来自控制台的输入和主方法中的参数。 main 方法为不同的控制台输入调用不同的方法,它为不同的参数调用不同的函数。所以我想通过模仿文件中的这些输入来用 Junit 测试这个主要方法。我该怎么做? junit中是否有专门的规定来测试类的main方法?

【问题讨论】:

  • 你不能测试一下处理方法吗?这样会更有效率。
  • 我已经测试了它们工作正常的方法,但我想测试主要方法。
  • Java 程序的main 方法就是……一个方法!它也可以通过任何其他方法调用(尤其是从测试方法中)。那你有什么问题呢?
  • 实际上我采用了相同的方法,但我在区分参数和命令行输入时遇到了问题。由于 main 只接受 String[] 并且它需要同时支持控制台输入和参数。

标签: java junit


【解决方案1】:

要提供来自文件的输入,请创建 FileInputStream 并将其设置为 System.in 流。您可能希望在 main 方法完成后将原始设置回原处,以确保以后使用它的任何东西仍然有效(其他测试,JUnit 本身......)

这是一个例子:

@Test
public void testMain() throws IOException {
    System.out.println("main");
    String[] args = null;
    final InputStream original = System.in;
    final FileInputStream fips = new FileInputStream(new File("[path_to_file]"));
    System.setIn(fips);
    Main.main(args);
    System.setIn(original);
}

在您的实际代码中,您需要处理任何 IOExceptions 并使用比文件的完整路径更好的东西(通过类加载器获取它),但这给了您大致的想法。


编辑:

几年和更多智慧之后,我不得不同意 Michael Lloyd Lee mlk's answer 作为更好的方法,如果可能的话应该首选什么。在包含您的 main 方法(甚至是单独的类)的类中,应该有一个接受任意 InputStream 和参数数组的方法。它可能是一个静态方法。然后main 方法只需使用System.in 流作为参数调用它。

public class Main {
    public static void main(String[] args) {
        start(System.in, args);
    }
    public static void start(InputStream input, String[] args) {
        // use input and args
    }
}

或者,您也可以有一个带有接受参数数组的构造函数的类,以创建一个正确配置的实例,然后在其上调用一个接受InputStream 的实例方法,如下所示:

public class Main {
    public static void main(String[] args) {
        Bootstrapper bootstrapper = new Bootstrapper(args);
        bootstrapper.start(System.in);
    }
}

public class Bootstrapper { // just some name, could be anything else

    // could have some instance fields here...
    
    public Bootstrapper(String[] args) {
        // use the args to configure this instance, set fields, get resources...
    }
    
    public void start(InputStream input) {
        // use input
    }
}

以最符合要求和程序设计的为准。在这两种情况下,您最终都会得到可以使用从文件中获得的InputStream 进行单元测试的东西。对Main.start 进行简单静态方法调用的测试,或创建引导程序实例然后调用其start 方法的测试。现在您的测试不再依赖于System.in 流。 main 方法所做的工作最少,并且足够简单,您无需单独测试即可相信它的正确性。在某些时候,一段代码变得如此微不足道,以至于测试它等同于不信任编译器。这些主要方法中的一个问题很快就会变得明显,因为它是您知道将始终被调用的一种方法。 main 方法做的太多或复杂,通常说明代码缺乏模块化。

我保留原来的答案,因为它确实提供了一个解决方案,以防您真的无法通过使用 System.in 并测试主要方法。这就是我首先写它的原因,因为我无法确定提问者的限制。有时有人只想直接回答他们的确切问题,因为没有更好的方法;不允许您更改代码,或者重构它需要付出太多努力并且没有预算,或者使用了一些硬编码的库以使用系统流等。请注意,原始答案无论如何都不太健壮。如果测试是并行运行而不是顺序运行,则换出系统流可能会导致意外和不一致的故障。

【讨论】:

  • 但是 fips 只会读取第一行。我怎样才能移到文件的下一行?
  • 当我使用Scanner 时,对我来说可以很好地处理多行文件。我想这取决于你的主要方法?
  • Main类属于什么包?
  • @acarlstein 无论您选择将 Main 类放入什么包。假设这是您自己编写的课程。在这么短的示例代码中,如果它们太明显,我会省略导入,如果它们对所解释的内容不重要,我会省略包名称。
【解决方案2】:

IMO 测试 main 方法的最佳方法是让 main 方法完全不做任何事情,只是设置世界并启动它。这样,一个简单的集成测试就给出了“世界已经建立”的答案。

然后所有其他问题变得更容易回答。

main 方法为不同的控制台输入调用不同的方法,它为不同的参数调用不同的函数。

它不应该,它应该调用someService.somemethod(args)。此服务与其他服务一样经过测试。

所以我想通过模仿文件中的这些输入来使用 Junit 测试这个主要方法。我该怎么做?

注入某种形式的假货或使用 TemporaryFolder JUnit 规则。

【讨论】:

  • 我经常看到这样的 cmets 建议“正确的事情”。虽然我始终同意,但请记住,我们很少知道情况的全部范围。有时,您的任务是测试别人的糟糕代码或您不能/不允许更改的遗留问题。
  • 虽然在这种情况下,“正确的方式”很可能是不以“正确的方式”去做,但我认为在答案列表中有正确的方式很重要。如果一个新的开发人员在这个页面上留下了残茬,并且看到了“错误的方式”而没有任何指向“正确方式”的指针,他们很可能会带着错误的想法离开。
  • 对此无可争辩。如果他在测试 main 方法,很可能是他写的。
【解决方案3】:

您可以像这样从 junit 测试中调用 main 方法:

YourClass.main(new String[] {"arg1", "arg2", "arg3"});

但由于 main 方法是 void 并且不返回任何内容,因此您应该测试在 main 调用后更改的对象;

这里是链接How do I test a method that doesn't return anything?

【讨论】:

  • 是的,我们可以这样调用,但是在执行此 main 函数之后会改变它的行为......我不想改变它。
  • @Test public void mainTest() throws Exception{ String programArgs = "myArgs --reuseIndex --dateRange "+todayDate.get(); MainClass.main(programArgs.split("\\s+")); }
猜你喜欢
  • 2019-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
相关资源
最近更新 更多