我有一个我想测试的Spring Batch文件处理程序。
SpringApplication.run()
是一个静态方法,我想验证传递给它的参数。
这是否意味着我需要沿着PowerMock路径走下去,或者SpringFramework中有什么东西可以让我测试它?
public File handleFile(File file) {
// Start the Batch Process and set the inputFile parameter
String[] args = {"--inputFile=" + file.getAbsolutePath()};
SpringApplication.run(InitialFileBatchApplication.class, args);
return null;
}
我的测试课程有以下注释,似乎无法正常工作:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@SpringBootTest
@PrepareForTest(SpringApplication.class)
我错过了什么?
抛出的异常是:
java.lang.IllegalStateException:无法转换名为org.springframework.boot.SpringApplication的类。原因:找不到 org.springframework.web.context.support.StandardServletEnvironment
处理@PrepareForTest(SpringApplication.class)
时会发生这种情况。我正在测试Spring Batch应用程序,因此没有Web环境,我也已经添加了。
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
答案 0 :(得分:1)
当我与PowerMock分享您的厌恶时,第一个答案很遗憾:您现在编写的方法 - 是只能使用PowerMock进行测试。
所以,如果你想测试那个方法;你必须使用PowerMock。或者你承担最小的风险......并且根本不进行测试。
除此之外:我建议将该方法放入某个界面;你只是想阻止当你开始测试想要调用handleFile()
的其他方法时,这个静态调用会给你带来麻烦 - 那么你希望能够模拟那个调用;防止内部静态调用发生。
答案 1 :(得分:1)
由于我遇到的异常,这个问题是由于pom.xml中缺少一个条目,这使我对SpringFramework感到沮丧,因为我只在批处理应用程序中工作并且没有web或servlet组件无论如何都在这个测试中。丢失的pom条目是。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
我所拥有的其他春天的家属
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
为了测试这个,我确实采用了PowerMock的方法,并将一些方法外部化,以便我可以测试它们,即使我正在使用Spring应用程序进行测试,我也能够排除加载了SpringRunner的SpringRunner。上下文简化此测试。下面是我的实现类以及测试它的测试类。
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
public class InitialFileInputFileHandler {
private Logger logger = LoggerFactory.getLogger(InitialFileInputFileHandler.class);
/**
* Handles the Initial Client files that get put into the input directory that match the pattern
* defined in initialFileListenerApplicationContext.xml
* @param file - The file
* @return
*/
public File handleFile(File file) {
logger.info("Got the Initial Client file: " + file.getAbsolutePath() + " start Batch Processing");
// Start the Batch Process and set the inputFile parameter
String[] args = buildArguments(file);
SpringApplication.run(InitialFileBatchApplication.class, args);
// Whatever we return is written to the outbound-channel-adapter.
// Returning null will not write anything out and we do not need an outbound-channel-adapter
return null;
}
protected String[] buildArguments(File file) {
String[] args = {"--inputFile=" + file.getAbsolutePath()};
return args;
}
}
这是测试类
import static org.mockito.Mockito.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.MatcherAssert.*;
import java.io.File;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;
// This test class must test static methods. One way to do that is with PowerMock.
// Testing with static methods so we have to run with the PowerMockRunner.
@RunWith(PowerMockRunner.class)
// The static method that we want to test is in the SpringApplication class so
// by using PowerMock we have to prepare this class for testing.
@PrepareForTest({SpringApplication.class})
// If you wanted to load a SpringContext you'd have to include the SpringRunner.
// Since our Runner is PowerMockRunner, we still have to setup the spring context, so
// you setup the SpringRunner as the delegate.
//@PowerMockRunnerDelegate(SpringRunner.class)
public class InitialFileInputFileHandlerTest {
// Setup a mockFile so that I can specify what comes back from the getAbsolutiePath method
// without actually to have a file on the file system.
@Mock File mockFile;
private InitialFileInputFileHandler handler;
@Before
public void setUp() throws Exception {
handler = new InitialFileInputFileHandler();
org.mockito.Mockito.when( mockFile.getAbsolutePath() ).thenReturn("src/input/fooFile.txt");
}
@Test
public void testBuildArguments(){
String[] args = handler.buildArguments(mockFile);
assertThat( args[0], equalTo("--inputFile=src/input/fooFile.txt") );
}
@Test
public void testHandleFile() throws Exception {
// Tell PowerMockito to keep track of my static method calls in the SpringApplication class
PowerMockito.mockStatic( SpringApplication.class );
// What I expect the argument to be
String[] args = {"--inputFile=src/input/fooFile.txt"};
// Call the actual method
handler.handleFile(mockFile);
// Have to call verifyStatic since its a static method.
PowerMockito.verifyStatic();
// One of a few possibilities to test the execution of the static method.
//SpringApplication.run( InitialFileBatchApplication.class, args);
//SpringApplication.run( Mockito.any(InitialFileBatchApplication.class), eq(args[0]));
SpringApplication.run( Mockito.any(Object.class), eq(args[0]));
}
}
答案 2 :(得分:0)
1.如果您要在测试中验证args
,则需要将其返回到方法handleFile(file)
的来电代码,目前您正在执行 - return null;
,而应返回args(如果方法签名可以更改)。
我假设handleFile
方法在InitialFileBatchApplication
类中。
@Test
public void testHandleFile() {
File file = new File("ABC");
String[] response = new InitialFileBatchApplication().handleFile(file);
//Verify response here
}
以上将实际开始你的工作。
2.如果你想模仿 - SpringApplication.run
,那么PowerMock就是你要走的路。您应该指出有问题的是您在当前设置中遇到的错误。
3.Mockito现在是在Spring Test中内置的,所以如果你可以重构你的代码以使非静态方法调用静态方法,那么你可以模拟非静态方法,并最终模拟你的静态调用。 @MockBean
注释是Spring Test的一部分。
4.如果在春季批次中模拟SpringApplication.run
等同于未运行作业,而只是初始化上下文,则可以通过spring.batch.job.enabled=false
application.properties
来实现目的。 1}}。只有你的单元测试必须等待真正的调用 - SpringApplication.run
完成,但工作不会开始。
除了功能正确之外,始终鼓励代码重构使您的代码单元可测试,因此请不要犹豫重构以克服框架限制。
希望它有所帮助!!