usr*_*ΛΩΝ 7 junit spring-test temporary-files spring-boot
我想做自洁测试
在我的情况下,我有一个依赖于目录的组件
public class FileRepositoryManagerImpl implements ....
@Value("${acme.fileRepository.basePath}")
private File basePath;
}
Run Code Online (Sandbox Code Playgroud)
该值在application.yml文件中定义,在 DEV 中它指向build.
这不是最糟糕的主意,因为gradle clean最终会清理测试造成的混乱。
但是,实际上,我想在这里实现的是确保每个测试都在一个隔离的临时目录中运行,该目录在执行后被清除。
我知道 JUnit 有一个用于临时目录的工具。但是一旦我在 JUnit 4 的范围内定义了该目录,我该如何告诉 Spring 使用该临时目录?
我尝试了内部类失败:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { SecurityBeanOverrideConfiguration.class, App.class })
@EnableConfigurationProperties
public abstract class AbstractFileRepositoryManagerIntTests {
private final static TemporaryFolder temporaryFolder = new TemporaryFolder();
@ClassRule
public static TemporaryFolder getTemporaryFolder()
{
return temporaryFolder;
}
@ConfigurationProperties(prefix = "acme")
static class Configuration
{
public FileRepository getFileRepository()
{
return new FileRepository();
}
static class FileRepository
{
public File basePath() throws Exception
{
return temporaryFolder.newFolder("fileRepositoryBaseDir");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在想修补与Environment,但什么应该是在春季启动测试程序注入性质的正确方法是什么?
Dmi*_*tov 13
如果您使用 JUnit 5.4+,那么您可以利用它们,@TempDir无需对目录进行手动生命周期管理即可正常工作。也就是说,与 JUnit 4 相比,您不需要手动创建和删除它@TemporaryFolder。
以下是如何实现目标的工作示例:
//Your bean into which you want to inject the property
@Component
public class FileRepositoryManager {
@Value("${acme.fileRepository.basePath}")
private File basePath;
public File getBasePath() {
return basePath;
}
}
//Test that uses ApplicationContextInitializer machinery to set the desired properties
@SpringBootTest
@ContextConfiguration(initializers = Initializer.class)
class FileRepositoryManagerTest {
@TempDir
static File tempDir;
@Autowired
FileRepositoryManager fileRepositoryManager;
@Test
void basePathIsSet() {
assertNotNull(fileRepositoryManager.getBasePath());
}
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
TestPropertyValues.of(
"acme.fileRepository.basePath=" + tempDir
).applyTo(context);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我可以想到至少四种不同的方法来解决您的问题。都有自己的优点和缺点。
方法一:ReflectionTestUtils
您正在@Value对私有实例属性使用注释(请不要再使用了!)。因此,您不能acme.fileRepository.basePath在没有反思的情况下即时更改。
package demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import java.io.File;
@SpringBootApplication
public class FileRepositoryApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryApp.class, args);
}
@Component
public class FileRepository {
@Value("${acme.fileRepository.basePath}")
private File basePath;
public File getBasePath() {
return basePath;
}
}
}
Run Code Online (Sandbox Code Playgroud)
改变basePath每个测试之后ReflectionTestUtils.setField。因为我们使用的是 Spring 的TestExecutionListener,它在 Junit 规则初始化之前被初始化,我们被迫管理beforeTestExecutionand 中的临时文件夹afterTestMethod。
package demo;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.IOException;
import static junit.framework.TestCase.assertEquals;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileRepositoryApp.class)
@TestExecutionListeners(listeners = FileRepositoryAppTest.SetBasePath.class, mergeMode = MERGE_WITH_DEFAULTS)
public class FileRepositoryAppTest {
private static TemporaryFolder temporaryFolder = new TemporaryFolder();
@Autowired
private FileRepositoryApp.FileRepository fileRepository;
@Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(fileRepository.getBasePath());
assertEquals(temporaryFolder.getRoot(), fileRepository.getBasePath());
}
@Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(fileRepository.getBasePath());
assertEquals(temporaryFolder.getRoot(), fileRepository.getBasePath());
}
static class SetBasePath implements TestExecutionListener {
@Override
public void beforeTestExecution(TestContext testContext) throws IOException {
temporaryFolder.create();
if (testContext.hasApplicationContext()) {
FileRepositoryApp.FileRepository bean = testContext.getApplicationContext().getBean(FileRepositoryApp.FileRepository.class);
ReflectionTestUtils.setField(bean, "basePath", temporaryFolder.getRoot());
}
}
@Override
public void afterTestMethod(TestContext testContext) {
temporaryFolder.delete();
}
}
}
Run Code Online (Sandbox Code Playgroud)
方法 2:配置属性
为您的应用程序配置引入配置属性类。它免费为您提供类型安全,我们不再依赖反射。
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
@SpringBootApplication
public class FileRepositoryWithPropertiesApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryWithPropertiesApp.class, args);
}
@Component
public class FileRepository {
private final FileRepositoryProperties fileRepositoryProperties;
public FileRepository(FileRepositoryProperties fileRepositoryProperties) {
this.fileRepositoryProperties = fileRepositoryProperties;
}
public File getBasePath() {
return fileRepositoryProperties.getBasePath();
}
}
@Component
@ConfigurationProperties(prefix = "acme.file-repository")
public class FileRepositoryProperties {
private File basePath;
public File getBasePath() {
return basePath;
}
public void setBasePath(File basePath) {
this.basePath = basePath;
}
}
}
Run Code Online (Sandbox Code Playgroud)
因为我们使用的是 Spring 的TestExecutionListener,它在 Junit 规则初始化之前被初始化,我们被迫管理beforeTestExecutionand 中的临时文件夹afterTestMethod。
package demo;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import static junit.framework.TestCase.assertEquals;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileRepositoryWithPropertiesApp.class)
@TestExecutionListeners(listeners = FileRepositoryWithPropertiesTest.SetBasePath.class, mergeMode = MERGE_WITH_DEFAULTS)
public class FileRepositoryWithPropertiesTest {
private static TemporaryFolder temporaryFolder = new TemporaryFolder();
@Autowired
private FileRepositoryWithPropertiesApp.FileRepository bean;
@Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
@Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
static class SetBasePath implements TestExecutionListener {
@Override
public void beforeTestExecution(TestContext testContext) throws IOException {
temporaryFolder.create();
if (testContext.hasApplicationContext()) {
FileRepositoryWithPropertiesApp.FileRepositoryProperties bean = testContext.getApplicationContext().getBean(FileRepositoryWithPropertiesApp.FileRepositoryProperties.class);
bean.setBasePath(temporaryFolder.getRoot());
}
}
@Override
public void afterTestMethod(TestContext testContext) {
temporaryFolder.delete();
}
}
}
Run Code Online (Sandbox Code Playgroud)
方法 3:重构你的代码(我最喜欢的)
提取basePath到它自己的类并将其隐藏在 api 后面。现在您不再需要查看您的应用程序属性和临时文件夹。
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
@SpringBootApplication
public class FileRepositoryWithAbstractionApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryWithAbstractionApp.class, args);
}
@Component
public class FileRepository {
private final FileRepositorySource fileRepositorySource;
public FileRepository(FileRepositorySource fileRepositorySource) {
this.fileRepositorySource = fileRepositorySource;
}
public File getBasePath() {
return fileRepositorySource.getBasePath();
}
}
@Component
public class FileRepositorySource {
private final FileRepositoryProperties fileRepositoryProperties;
public FileRepositorySource(FileRepositoryProperties fileRepositoryProperties) {
this.fileRepositoryProperties = fileRepositoryProperties;
}
// TODO for the sake of brevity no real api here
public File getBasePath() {
return fileRepositoryProperties.getBasePath();
}
}
@Component
@ConfigurationProperties(prefix = "acme.file-repository")
public class FileRepositoryProperties {
private File basePath;
public File getBasePath() {
return basePath;
}
public void setBasePath(File basePath) {
this.basePath = basePath;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们不再需要任何额外的测试设施,我们可以使用@RuleonTemporaryFolder代替。
package demo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileRepositoryWithAbstractionApp.class)
public class FileRepositoryWithAbstractionTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@MockBean
private FileRepositoryWithAbstractionApp.FileRepositorySource fileRepositorySource;
@Autowired
private FileRepositoryWithAbstractionApp.FileRepository bean;
@Before
public void setUp() {
when(fileRepositorySource.getBasePath()).thenReturn(temporaryFolder.getRoot());
}
@Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
@Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
}
Run Code Online (Sandbox Code Playgroud)
方法四:TestPropertySource
使用 Spring 的TestPropertySource注释有选择地覆盖测试中的属性。由于 Java 注释不能具有动态值,因此您需要事先决定要在何处创建目录,并记住由于使用了 os 路径分隔符,您的测试绑定到特定操作系统。
package demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static demo.FileRepositoryTestPropertySourceTest.BASE_PATH;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FileRepositoryApp.class)
@TestPropertySource(properties = "acme.fileRepository.basePath=" + BASE_PATH)
public class FileRepositoryTestPropertySourceTest {
static final String BASE_PATH = "/tmp/junit-base-path";
private Path basePath = Paths.get(BASE_PATH);;
@Autowired
private FileRepositoryApp.FileRepository fileRepository;
@Before
public void setUp() throws IOException {
Files.deleteIfExists(basePath);
Files.createDirectories(basePath);
}
@After
public void after() throws IOException {
Files.deleteIfExists(basePath);
}
@Test
public void method() {
System.out.println(fileRepository.getBasePath());
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2788 次 |
| 最近记录: |