Интегрируем YAML и тестовый фреймворк

Новая заметка, в которой я покажу, как можно использовать файлы с разширением .yml в качестве конфигурационых файлов для тестового фреймворка. Хочу отметить, что я уже писал на подобную тему, только тогда применял Groovy файл.

yaml logo

Давайте посмотрим на суть задачи. Наверняка те, кто писал фреймворки на Java, пользовался стандартными файлами c расширением .proprties. Наличие проперти файлов - это правило хорошего тона.

Вся конфигурационная информация выносится за переделы кода, откуда ее можно легко изменять. Вот, скажем, стандартный пример из жизни: у нас есть несколько окружений, на которых мы запускаем наши тесты. Для каждого окружения нужны различные парметры: хост, порт и т.п. Зачастую люди делают либо разные проперти файлы и пишут Factory pattern для того, чтобы считывать из них информацию, либо делают один проперти файл, который зачастую выглядит так:

#dev env
dev.server.host=localhost
dev.server.port=7070

#qa env
qa.server.host=qahost
qa.server.port=8081

Затем пишется все та же Factory для считывания данного файла. Чего греха таить, я, бывало, тоже такое писал. Но пришло время совершенствоваться и использовать более элегантные подходы. И здесь на помощь приходит язык разметки YAML. Что это и для чего придумано, читать в Википедии ;)

Мы можем легко и просто использовать .yml файлы как замену для проперти файлов c помощью библиотеки snakeyml.

Создаем config.yml файл:

environment:
  dev:
    server.host: http://localhost:7070
    db.url: jdbc:usernarm:userpass@test
  qa:
    server.host: http://qa.test:8080

Теперь напишем класс, который будет считывать наш файл:

public class YamlReader {

    public Map<String, Object> read(String path) throws IOException {
        InputStream in = Files.newInputStream(Paths.get(path));
        Yaml yaml = new Yaml();
        return (Map<String, Object>) yaml.load(in);
    }
}

Читаем файл:

public static void main(String[] args) throws IOException {
        YamlReader reader = new YamlReader();
        Map<String, Object> config = reader.read("src/main/resources/config.yml");
        System.out.println(config);
}

Console output:
{environment={dev={server.host=http://localhost:7070, db.url=jdbc:usernarm:userpass@test}, qa={server.host=http://qa.test:8080}}}

Нам возвращается Map, с которой мы можем дальше работать. Но здесь есть один недостаток - casting. Нам нужно кастить каждый раз, когда мы будем доставать значение из map.

Есть гораздо лучшее решение. Cоздаем класс Configuration следующего содержания:

public class Configuration {
    public Map<String,Map<String,String>> environment;
}

Создаем метод в ридере:

public <T> T read(String path, Class<T> c) throws IOException {
        Yaml yaml = new Yaml();
        try (InputStream in = Files.newInputStream(Paths.get(path))) {
            return yaml.loadAs(in, c);
  }
}

Используем новый метод для чтения проперти:

 YamlReader reader = new YamlReader();
 Configuration read = reader.read("src/main/resources/config.yml", Configuration.class);

 Map<String, String> map = read.environment.get("qa");

Как вы могли заметить, второй подход более элегантен и удобен. Правда, вы не сможете использовать никакой другой конструкции, кроме Map; в первом же случае вы вольны кастить значения к тому типу, к которому душе угодно. На этом у меня все. Подписывайтесь, чтобы держать руку на пульсе.