是否有可能从junit测试中获取main方法中的变量?

时间:2015-09-16 07:40:51

标签: java variables junit

在Junit测试中,是否可以在main方法中测试变量?

我的主要方法是这样的:

package edu.blah.class.project1;

public class Program {

public static void main(String[] args) {
    try {
        int result = Utility.analyze();
    } catch (Exception e) {
        System.out.println("Error");
    }
    }

}

是否可以在JUnit类中获取变量结果?是使它成为公共变量的唯一方法吗?谢谢!

3 个答案:

答案 0 :(得分:3)

不,局部变量在堆栈上分配,在方法外部不可用。

您希望从测试中检查局部变量意味着您可能应该将方法拆分为几个较小的方法。通常,这是TDD - 测试驱动开发的主要原则之一。

答案 1 :(得分:3)

  

在Junit测试中,是否可以在main方法中测试变量?

呃......不。除非您有一个明确调用main方法的JUnit测试,否则不会在单元测试中调用main方法。

此外,这里的变量是result,它是一个局部变量,并且无法进入测试某个局部变量的方法。 (Java根本就不允许......)

  

是否可以在JUnit类中获取变量结果?

在此示例中,result获取了调用Utility.analyze()的值。没有理由为什么JUnit测试也不能这样做......从而得到相同的值。 (或者至少它可以在这个例子中。)

  

是使它成为公共变量的唯一方法吗?

不......见上文。

但是,如果您实际上是尝试在主方法的上下文中测试result 的值,那么您是对的。获取值的唯一方法是将其公开为static变量。但它不一定必须是public static。 (事实上​​,如果你准备写一些"讨厌"反射,你甚至可以通过JUnit测试来提取和测试private static字段的值。)

但是,我认为你采取了错误的做法。而不是试图弄清楚如何进入静态方法(或任何方法),如果您重新构建代码以使其更易于测试,那将更好。例如:

package edu.blah.class.project1;

public class Program {

    public int run(String args[]) {
        return Utility.analyze();
    }

    public static void main(String[] args) {
        try {
            new Program().run(args);
        } catch (Exception e) {
            System.out.println("Error");
        }
    }
}

通过少量重组(如此),您可以更轻松地为所有重要功能编写单元测试。你留下了"问题"仍然很难对main方法进行单元测试。但是,您已将方法简化为非常简单,以便通过目视检查验证它是否正确:现在不需要单元测试main

答案 2 :(得分:1)

单元测试可以调用任何可访问的方法,包括main方法。除了被认为是应用程序入口点之外,主要方法没有什么特别之处。

关于如何测试您的示例,在当前状态下无法访问局部变量result。在使用各种技巧来访问它之前,问问自己为什么要测试它的问题?

单元测试应检查方法的合同,包括输入参数,返回值和任何副作用。可测试的副作用是:

  • 更改包含类的状态
  • 调用作为测试类中字段的类的实例

很难测试副作用:

  • 静态通话
  • 直接与环境交互,如文件系统,网络,线程
  • 行为取决于系统时间

在大多数情况下,通过重构您的应用程序可以避免难以测试的情况,使其更易于测试。

但是,在您的示例中,以上都不适用。本地变量'结果'什么都不用。因此,在您申请的当前状态下,您不必(也不能)测试其价值。

当我用我的想象力来猜测你的应用程序可以/应该做什么时,扩展和可测试的版本可能看起来像这样:

public class Program {

    public static final String ERROR_MESSAGE = "Error";

    // Utility modeled as a dependency to avoid static access
    private Utility utility;

    // a poor man's logger, better to use a logging framework like log4j
    private PrintStream logger;

    // 'business logic' extracted into a separate method to be tested
    public void execute(String[] args) {
        try {
            // static access to Utility replaced with instance access
            // passing the args to make testing more interesting
            int result = utility.analyze(args);
            // added logging of the result to make testing more interesting
            logger.println(result);
        } catch (Exception e) {
            // Static access to System.out replaced with logger instance
            logger.println(ERROR_MESSAGE);
        }
    } 

    // setters used for dependency injection
    public void setUtility(Utility utility) {
        this.utility = utility;
    }

    public void setLogger(PrintStream logger) {
        this.logger = logger;
    }

    // application entry point does basic initalization and depency injection
    public static void main(String[] args) {
        // create application instance
        Program program = new Program();

        // inject dependencies
        program.setUtility(new Utility());
        program.setLogger(System.out);

        // call application
        program.execute(args);
    }
}

单元测试,使用JUnit4:

import static org.mockito.Mockito.*;
import org.junit.*;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.*;

@RunWith(MockitoJUnitRunner.class)
public class ProgramTest {

    // @InjectMocks creates an instance and injects any specified mocks
    @InjectMocks
    private Program instance;
    // @Mock creates a mock instance, which can be used to specify behavior using when() and verify access using verify()
    @Mock
    private Utility utility;
    @Mock
    private PrintStream printStream;

    // @Test indicates a test method in JUnit4
    @Test
    public void testExecuteHappy() {

        // SETUP
        String[] args = new String[];
        int expectedResult = 42;
        // specify behavior of the Utility mock to return the expected result
        when(utility.analyze()).thenReturn(expectedResult);

        // CALL
        instance.execute(args);

        // VERIFY
        // check that utility.analyse() was called with the args
        verify(utility).analyze(args);
        // verify that logger.println() was called with the expected result
        verify(logger).println(expectedResult);
    }

    @Test
    public void testExecuteError() {

        // SETUP
        String[] args = new String[];
        // specify behavior of the Utility mock to throw an exception
        when(utility.analyze()).doThrow(new Exception("test exception));

        // CALL
        instance.execute(args);

        // VERIFY
        // check that utility.analyse() was called with the args
        verify(utility).analyze(args);
        // verify that logger.println() was called with the error message
        verify(logger).println(Program.ERROR_MESSAGE);
    }
}

通常,依赖关系需要额外的配置,例如访问哪个数据库,连接池等。在较大的应用程序中,依赖注入将由SpringCDI等框架完成,而不是主方法。

以下是此需要的Maven依赖项:

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>

将这些添加到您的pom.xml中。如果您不使用Maven,请下载这些.jar文件并将其添加到用于编译的类路径中:

注意:我没有编译或测试上面的代码,所以可能会有小错误