在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类中获取变量结果? 是使其成为公共变量的唯一方法吗? 谢谢!
看看这个问题:stackoverflow.com/questions/1119385/
该变量在实际产品中有什么用? 似乎没有必要...
In a Junit test, is it possible to test for variables in the main method?
嗯...不除非您有一个JUnit测试明确地调用main方法,否则在单元测试中不会调用main方法。
此外,这里的变量是result,它是一个局部变量,无法进入测试某些局部变量的方法。 (Java根本不允许它...)
Is it possible to obtain the variable result in a JUnit class?
在此示例中,result获取调用Utility.analyze()的值。没有理由为什么JUnit测试也不能这样做……从而获得相同的值。 (或者至少在此示例中可以。)
Is the only way to make it a public variable?
不...请参见上文。
但是,如果您实际上试图在main方法的上下文中测试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。
不可以,局部变量是在堆栈上分配的,在方法之外不可用。
您希望从测试中检查局部变量意味着您可能应该将方法拆分为几个较小的方法。通常,这是TDD(测试驱动开发)的原理之一。
单元测试可以调用任何可访问的方法,包括main方法。除了将其识别为应用程序入口点以外,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);
}
}
通常,依赖项需要额外的配置,例如要访问的数据库,连接池等。在更大的应用程序中,依赖项注入将由Spring或CDI之类的框架完成,而不是由main方法完成。
这是为此所需的Maven依赖项:
junit
junit
4.11
org.mockito
mockito-core
1.9.5
将它们添加到您的pom.xml中。如果您不使用Maven,请下载以下.jar文件并将其添加到用于编译的类路径中:
朱尼特4.11
Mockito核心1.9.5
注意:我没有编译或测试上面的代码,因此可能会有小的错字