Saltar al contenido

En Spring Boot Test, ¿cómo asigno una carpeta temporal a una propiedad de configuración?

Solución:

Puedo pensar en al menos cuatro enfoques diferentes para su problema. Todo con sus propias ventajas y desventajas.

Enfoque 1: ReflectionTestUtils

Tu estas usando @Value anotación en una propiedad de instancia privada (por favor, ¡no lo hagas más!). Por tanto, no puedes cambiar acme.fileRepository.basePath sobre la marcha sin reflejo.

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;
        }
    }
}

Cambiando basePath después de cada prueba con ReflectionTestUtils.setField. Debido a que estamos usando TestExecutionListener de Spring, que se inicializa antes de que se inicialicen las reglas de Junit, nos vemos obligados a administrar la carpeta temporal en beforeTestExecution y 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();
        }
    }
}



Enfoque 2: propiedades de configuración

Introduzca una clase de propiedades de configuración para la configuración de su aplicación. Le brinda seguridad de tipografía de forma gratuita y ya no confiamos en la reflexión.

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;
        }
    }

}

Debido a que estamos usando el TestExecutionListener de Spring, que se inicializa antes de que se inicialicen las reglas de Junit, nos vemos obligados a administrar la carpeta temporal en beforeTestExecution y 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();
        }
    }
}

Enfoque 3: Refactorice su código (mi favorito)

Extraer basePath en su propia clase y esconderlo detrás de una API. Ahora ya no necesita tocar las propiedades de su aplicación y una carpeta temporal.

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;
        }
    }
}

Ya no necesitamos ninguna instalación de prueba adicional y podemos usar @Rule sobre TemporaryFolder en lugar de.

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());
    }

}

Enfoque 4: TestPropertySource

Use la anotación TestPropertySource de Spring para anular las propiedades en una prueba de forma selectiva. Debido a que una anotación de Java no puede tener un valor dinámico, debe decidir de antemano dónde desea crear su directorio y tener en cuenta que su prueba está vinculada a un sistema operativo específico debido al separador de ruta del sistema operativo utilizado.

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());
    }
}
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *