我如何模拟SpringApplication.run

时间:2017-01-03 19:55:19

标签: mockito spring-batch powermock

我有一个我想测试的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)

3 个答案:

答案 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完成,但工作不会开始。

除了功能正确之外,始终鼓励代码重构使您的代码单元可测试,因此请不要犹豫重构以克服框架限制。

希望它有所帮助!!

相关问题