Aktifkan @PropertySource menggunakan YAML

107

Spring Boot memungkinkan kita mengganti file application.properties dengan yang setara dengan YAML. Namun saya tampaknya menemui hambatan dengan tes saya. Jika saya memberi anotasi TestConfiguration(konfigurasi Java sederhana) saya, ia mengharapkan file properti.

Misalnya ini tidak berhasil: @PropertySource(value = "classpath:application-test.yml")

Jika saya memiliki ini di file YAML saya:

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword

Dan saya akan memanfaatkan nilai-nilai itu dengan sesuatu seperti ini:

@Value("${db.username}") String username

Namun, saya berakhir dengan kesalahan seperti ini:

Could not resolve placeholder 'db.username' in string value "${db.username}"

Bagaimana saya dapat memanfaatkan kebaikan YAML dalam pengujian saya juga?

checketts
sumber
Tentukan "tidak berhasil". Apa pengecualian / kesalahan / peringatannya?
Emerson Farrugia
Spring Boot meratakan file YAML sehingga muncul sebagai file properti dengan notasi titik. Perataan itu tidak terjadi.
checketts
Dan hanya untuk mengonfirmasi, ini berfungsi dalam kode non-tes?
Emerson Farrugia
1
Iya. Berikut adalah dokumen yang menjelaskan projects.spring.io/spring-boot/docs/spring-boot-actuator/… dan di bawah halaman tertulis 'Perhatikan bahwa objek YAML diratakan menggunakan pemisah titik.'
checketts
9
SpingBoot mengatakan bahwa ia tidak dapat memuat YAML dengan PropertySource: File YAML kekurangan 24.6.4 YAML tidak dapat dimuat melalui anotasi @PropertySource. Jadi jika Anda perlu memuat nilai dengan cara itu, Anda perlu menggunakan file properti.
Lex Pro

Jawaban:

55

Spring-boot memiliki bantuan untuk ini, tambahkan saja

@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)

di bagian atas kelas pengujian Anda atau superclass pengujian abstrak.

Sunting: Saya menulis jawaban ini lima tahun lalu. Itu tidak bekerja dengan versi terbaru dari Spring Boot. Inilah yang saya lakukan sekarang (harap terjemahkan Kotlin ke Java jika perlu):

@TestPropertySource(locations=["classpath:application.yml"])
@ContextConfiguration(
        initializers=[ConfigFileApplicationContextInitializer::class]
)

ditambahkan ke atas, lalu

    @Configuration
    open class TestConfig {

        @Bean
        open fun propertiesResolver(): PropertySourcesPlaceholderConfigurer {
            return PropertySourcesPlaceholderConfigurer()
        }
    }

ke konteksnya.

Ola Sundell
sumber
3
jangan lupa PropertySourcesPlaceholderConfigurer
Kalpesh Soni
Memang @KalpeshSoni, tanpa kata Configurer, itu tidak akan berhasil.
Ola Sundell
Saya harus menambahkan penginisialisasi ke @SpringJunitConfig sebagai gantinya@SpringJUnitConfig(value = {...}, initializers = {ConfigFileApplicationContextInitializer.class})
Tomas F
1
@ Jan Galinski Anda dapat mencoba jawaban saya, mudah digunakan, dan berfungsi dengan baik di prod env saya. stackoverflow.com/questions/21271468/…
Forest10
59

Seperti yang disebutkan @PropertySourcetidak memuat file yaml. Sebagai solusinya, muat file Anda sendiri dan tambahkan properti yang dimuat ke Environment.

Penerapan ApplicationContextInitializer:

public class YamlFileApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    try {
        Resource resource = applicationContext.getResource("classpath:file.yml");
        YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
        PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null);
        applicationContext.getEnvironment().getPropertySources().addFirst(yamlTestProperties);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
  }
}

Tambahkan penginisialisasi Anda ke pengujian Anda:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class, initializers = YamlFileApplicationContextInitializer.class)
public class SimpleTest {
  @Test
  public test(){
    // test your properties
  }
}
Mateusz Balbus
sumber
Sebenarnya ini harus menjadi jawaban terbaik, terima kasih berhasil!
Adelin
Mateusz, saya telah memposting jawaban dengan YamlFileApplicationContextInitializerkelas di mana lokasi YAML ditentukan per kasus uji. Jika menurut Anda itu menarik, silakan menggabungkannya ke dalam jawaban Anda dan saya akan menghapus milik saya. Beri tahu saya di komentar di bawah jawaban saya.
Michal Foksa
Ya, ini adalah jawaban terbaik
Richard HM
34

@PropertySourcedapat dikonfigurasi dengan factoryargumen. Jadi Anda bisa melakukan sesuatu seperti:

@PropertySource(value = "classpath:application-test.yml", factory = YamlPropertyLoaderFactory.class)

Di mana YamlPropertyLoaderFactorypemuat properti khusus Anda:

public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        if (resource == null){
            return super.createPropertySource(name, resource);
        }

        return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null);
    }
}

Terinspirasi oleh https://stackoverflow.com/a/45882447/4527110

Сергей Варюхин
sumber
2
Parse yaml yang mendasari ini melempar IllegalStateExceptionketika file tidak ada dan bukan yang tepat FileNotFoundException- jadi untuk membuat ini berfungsi @PropertySource(..., ignoreResourceNotFound = true), Anda perlu menangkap dan menangani kasus ini: try { return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource(), null); } catch (IllegalStateException e) { throw (IOException) e.getCause(); }
Christian Opitz
2
Jika Anda perlu mendapatkan properti untuk profil tertentu, parameter ketiga di YamlPropertySourceLoader.load () adalah nama profil. YamlPropertySourceLoader.load () telah berubah untuk mengembalikan daftar daripada satu sumber properti. Berikut info lebih lanjut stackoverflow.com/a/53697551/10668441
pcoates
1
Ini adalah pendekatan terbersih sejauh ini.
Michal Foksa
7
bagi saya, diperlukan sedikit modifikasi sebagai berikut:CompositePropertySource propertySource = new CompositePropertySource(name); new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).stream().forEach(propertySource::addPropertySource); return propertySource;
xorcus
28

@PropertySourcehanya mendukung file properti (ini adalah batasan dari Spring, bukan Boot itu sendiri). Jangan ragu untuk membuka tiket permintaan fitur di JIRA .

Dave Syer
sumber
Saya berharap ada cara untuk menggunakan kembali pendengar yaml atau memuat yaml secara manual di Lingkungan yang dapat diteruskan ke konfigurasi pengujian.
checketts
10
Saya kira Anda dapat menulis ApplicationContextInitializerdan menambahkannya ke konfigurasi pengujian (cukup gunakan a YamlPropertySourceLoaderuntuk meningkatkan Environment). Secara pribadi saya lebih suka jika @PropertySourcemendukung perilaku ini secara asli.
Dave Syer
apakah ini masih terjadi? apakah '@PropertySource' tidak mendukung YAML?
domi
1
stackoverflow.com/questions/21271468/… gunakan ini dapat menyelesaikan @PropertySource hanya mendukung file properti
Forest10
Saya terkejut saya menyelesaikan masalah saya dengan posting berusia 6 tahun ini.
Jin Kwon
20

Pilihan lainnya adalah mengatur spring.config.locationmelalui @TestPropertySource:

@TestPropertySource(properties = { "spring.config.location = classpath:<path-to-your-yml-file>" }
Doc Davluz
sumber
3
Saya telah mengukur masukan dengan baris berikut: @TestPropertySource(properties = {"spring.config.location=classpath:application-${test.env}.yml" }) IMO milik Anda adalah jawaban terbaik dari semuanya.
leventunver
1
Ide bagus dan sangat minimalis untuk ujian, terima kasih banyak! Sekadar menambahkan, seseorang dapat menyertakan beberapa file konfigurasi, sesuai:@TestPropertySource(properties = {"spring.config.location=classpath:application-config.yml,classpath:test-config.yml,..." })
stx
1
Ini adalah jawaban terbaik sejauh ini! perhatikan bahwa Anda perlu memiliki @SpringBootTestanotasi
Mistriel
Ini bekerja secara ajaib!
pengguna1079877
19

Dari Spring Boot 1.4, Anda dapat menggunakan @SpringBootTestanotasi baru untuk melakukannya dengan lebih mudah (dan untuk menyederhanakan pengaturan pengujian integrasi Anda secara umum) dengan melakukan bootstrap pada pengujian integrasi Anda menggunakan dukungan Spring Boot.

Detail di Blog Musim Semi .

Sejauh yang saya tahu, ini berarti Anda mendapatkan semua manfaat dari kebaikan konfigurasi eksternal Spring Boot seperti dalam kode produksi Anda, termasuk secara otomatis mengambil konfigurasi YAML dari classpath.

Secara default, anotasi ini akan

... upaya pertama untuk memuat @Configurationdari kelas dalam mana pun, dan jika gagal, @SpringBootApplicationkelas utama Anda akan dicari .

tetapi Anda dapat menentukan kelas konfigurasi lain jika diperlukan.

Untuk kasus ini, Anda dapat menggabungkan @SpringBootTestdengan @ActiveProfiles( "test" )dan Spring akan mengambil konfigurasi YAML Anda, asalkan mengikuti standar Boot penamaan normal (yaitu application-test.yml).

@RunWith( SpringRunner.class )
@SpringBootTest
@ActiveProfiles( "test" )
public class SpringBootITest {

    @Value("${db.username}")
    private String username;

    @Autowired
    private MyBean myBean;

    ...

}

Catatan: SpringRunner.classadalah nama baru untukSpringJUnit4ClassRunner.class

moogpwns
sumber
1
:) Menggunakan @ActiveProfiles adalah satu-satunya opsi yang berhasil. Terima kasih!
zcourts
10

Pendekatan untuk memuat properti yaml, IMHO dapat dilakukan dengan dua cara:

Sebuah. Anda dapat meletakkan konfigurasi di lokasi standar - application.ymldi classpath root - biasanya src/main/resourcesdan properti yaml ini akan dimuat secara otomatis oleh Spring boot dengan nama jalur yang diratakan yang telah Anda sebutkan.

b. Pendekatan kedua sedikit lebih ekstensif, pada dasarnya tentukan kelas untuk menampung properti Anda dengan cara ini:

@ConfigurationProperties(path="classpath:/appprops.yml", name="db")
public class DbProperties {
    private String url;
    private String username;
    private String password;
...
}

Jadi pada dasarnya ini adalah mengatakan bahwa memuat file yaml dan mengisi kelas DbProperties berdasarkan elemen root "db".

Sekarang untuk menggunakannya di kelas mana pun, Anda harus melakukan ini:

@EnableConfigurationProperties(DbProperties.class)
public class PropertiesUsingService {

    @Autowired private DbProperties dbProperties;

}

Salah satu dari pendekatan ini akan bekerja untuk Anda dengan bersih menggunakan Spring-boot.

Biju Kunjummen
sumber
Pastikan Anda memiliki snakeyml di classpath Anda dan cara di atas harus berfungsi.
hoserdude
3
Hari-hari ini (meskipun tidak pada saat pertanyaan ini diajukan), snakeyamlditarik sebagai ketergantungan transitif oleh spring-boot-starter, jadi seharusnya tidak perlu menambahkannya ke Anda pom.xmlatau build.gradle, kecuali Anda memiliki keinginan yang mengakar untuk menggunakan versi yang berbeda. :)
Steve
2
Sekarang locationsbukan path, dan ConfigFileApplicationContextInitializeritu juga wajib.
OrangeDog
3

Saya menemukan solusi dengan menggunakan @ActiveProfiles("test")dan menambahkan file application-test.yml ke src / test / resources.

Itu akhirnya terlihat seperti ini:

@SpringApplicationConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)
@ActiveProfiles("test")
public abstract class AbstractIntegrationTest extends AbstractTransactionalJUnit4SpringContextTests {

}

File application-test.yml hanya berisi properti yang ingin saya timpa dari application.yml (yang dapat ditemukan di src / main / resources).

Poli
sumber
Inilah yang saya coba gunakan juga. Untuk beberapa alasan itu tidak berfungsi (Spring Boot 1.3.3) saat saya gunakan @Value("${my.property}")tetapi berfungsi dengan baik jika saya gunakan environment.getProperty("my.property").
martin-g
1

itu karena Anda belum mengkonfigurasi snakeyml. musim semi boot datang dengan fitur @EnableAutoConfiguration. ada konfigurasi snakeyml juga ketika Anda memanggil anotasi ini ..

Ini cara saya:

@Configuration
@EnableAutoConfiguration
public class AppContextTest {
}

ini tes saya:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(
        classes = {
                AppContextTest.class,
                JaxbConfiguration.class,
        }
)

public class JaxbTest {
//tests are ommited
}
pengguna2582794
sumber
0

Saya perlu membaca beberapa properti ke dalam kode saya dan ini berfungsi dengan spring-boot 1.3.0.RELEASE

@Autowired
private ConfigurableListableBeanFactory beanFactory;

// access a properties.yml file like properties
@Bean
public PropertySource properties() {
    PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
    yaml.setResources(new ClassPathResource("properties.yml"));
    propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
    // properties need to be processed by beanfactory to be accessible after
    propertySourcesPlaceholderConfigurer.postProcessBeanFactory(beanFactory);
    return propertySourcesPlaceholderConfigurer.getAppliedPropertySources().get(PropertySourcesPlaceholderConfigurer.LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME);
}
UV
sumber
0

Memuat file yml kustom dengan konfigurasi beberapa profil di Spring Boot.

1) Tambahkan kacang properti dengan SpringBootApplication dimulai sebagai berikut

@SpringBootApplication
@ComponentScan({"com.example.as.*"})
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Bean
    @Profile("dev")
    public PropertySourcesPlaceholderConfigurer propertiesStage() {
        return properties("dev");
    }

    @Bean
    @Profile("stage")
    public PropertySourcesPlaceholderConfigurer propertiesDev() {
        return properties("stage");
    }

    @Bean
    @Profile("default")
    public PropertySourcesPlaceholderConfigurer propertiesDefault() {
        return properties("default");

    }
   /**
    * Update custom specific yml file with profile configuration.
    * @param profile
    * @return
    */
    public static PropertySourcesPlaceholderConfigurer properties(String profile) {
       PropertySourcesPlaceholderConfigurer propertyConfig = null;
       YamlPropertiesFactoryBean yaml  = null;

       propertyConfig  = new PropertySourcesPlaceholderConfigurer();
       yaml = new YamlPropertiesFactoryBean();
       yaml.setDocumentMatchers(new SpringProfileDocumentMatcher(profile));// load profile filter.
       yaml.setResources(new ClassPathResource("env_config/test-service-config.yml"));
       propertyConfig.setProperties(yaml.getObject());
       return propertyConfig;
    }
}

2) Konfigurasi objek Java pojo sebagai berikut

@Component
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
@ConfigurationProperties(prefix = "test-service")
public class TestConfig {

    @JsonProperty("id") 
    private  String id;

    @JsonProperty("name")
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   

}

3) Buat yml kustom (dan letakkan di bawah jalur sumber daya sebagai berikut, Nama file YML: test-service-config.yml

Misalnya Config di file yml.

test-service: 
    id: default_id
    name: Default application config
---
spring:
  profiles: dev

test-service: 
  id: dev_id
  name: dev application config

--- 
spring:
  profiles: stage

test-service: 
  id: stage_id
  name: stage application config
Arunachalam Govindasamy
sumber
0

Saya berada dalam situasi tertentu di mana saya tidak dapat memuat kelas @ConfigurationProperties karena penamaan properti file khusus. Pada akhirnya, satu-satunya hal yang berhasil adalah (terima kasih @Mateusz Balbus):

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MyTest.ContextConfiguration.class})
public class MyTest {

    @TestConfiguration
    public static class ContextConfiguration {

        @Autowired
        ApplicationContext applicationContext;

        @Bean
        public ConfigurationPropertiesBean myConfigurationPropertiesBean() throws IOException {
            Resource resource = applicationContext.getResource("classpath:my-properties-file.yml");

            YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
            List<PropertySource<?>> loadedSources = sourceLoader.load("yamlTestProperties", resource);
            PropertySource<?> yamlTestProperties = loadedSources.get(0);
            ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment)applicationContext.getEnvironment();
            configurableEnvironment.getPropertySources().addFirst(yamlTestProperties);

            Binder binder = Binder.get(applicationContext.getEnvironment());
            ConfigurationPropertiesBean configurationPropertiesBean = binder.bind("my-properties-file-prefix", Bindable.of(ConfigurationPropertiesBean.class)).get();
            return configurationPropertiesBean;
        }

    }

    @Autowired
    ConfigurationPropertiesBean configurationPropertiesBean;

    @Test
    public void test() {

        configurationPropertiesBean.getMyProperty();

    }

}
aldebaran-ms
sumber
0
<dependency>
  <groupId>com.github.yingzhuo</groupId>
  <artifactId>spring-boot-stater-env</artifactId>
  <version>0.0.3</version>
</dependency>

Selamat menggunakan perpustakaan saya. Sekarang yaml , toml , hocon didukung.

Sumber: github.com

Zhuo YING
sumber
0

Ini bukan jawaban untuk pertanyaan awal, tetapi solusi alternatif untuk kebutuhan memiliki konfigurasi yang berbeda dalam pengujian ...

Daripada @PropertySourceAnda bisa menggunakan -Dspring.config.additional-location=classpath:application-tests.yml.

Sadarilah, akhiran testsitu tidak berarti profil ...

Dalam satu file YAML, seseorang dapat menentukan beberapa profil, yang dapat diwariskan satu sama lain, baca lebih lanjut di sini - Penyelesaian properti untuk beberapa profil Spring (konfigurasi yaml)

Kemudian, Anda dapat menentukan dalam pengujian Anda, bahwa profil aktif (menggunakan @ActiveProfiles("profile1,profile2")) adalah profile1,profile2tempat yang profile2akan dengan mudah menimpa (beberapa, satu tidak perlu menimpa semua) properti dari profile1.

Betlista
sumber
0

Saya telah mencoba semua pertanyaan yang terdaftar, tetapi semuanya tidak berfungsi untuk tugas saya: menggunakan file yaml tertentu untuk beberapa unit test. Dalam kasus saya, ini berfungsi seperti ini:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(initializers = {ConfigFileApplicationContextInitializer.class})
@TestPropertySource(properties = {"spring.config.location=file:../path/to/specific/config/application.yml"})
public class SomeTest {


    @Value("${my.property.value:#{null}}")
    private String value;

    @Test
    public void test() {
        System.out.println("value = " + value);
    }

}
FedorM
sumber
-6

Tidak perlu menambahkan seperti YamlPropertyLoaderFactory atau YamlFileApplicationContextInitializer. Anda harus mengubah ide Anda. seperti proyek musim semi biasa. Anda tahu, tidak menggunakan konfigurasi Java. Hanya * .xml

Ikuti langkah ini:

Cukup tambahkan applicationContext.xml suka

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"
       default-autowire="byName">

    <context:property-placeholder location="classpath*:*.yml"/>
</beans>

lalu tambahkan

@ImportResource({"classpath:applicationContext.xml"})

untuk Anda ApplicationMainClass.

Ini dapat membantu memindai application-test.yml Anda

db:
  url: jdbc:oracle:thin:@pathToMyDb
  username: someUser
  password: fakePassword
Hutan 10
sumber
Pertanyaan itu terkait dengan yaml (yang merupakan metode konfigurasi IMHO yang baik)
aldebaran-ms