Как проверить переводчика с помощью JUnit? - PullRequest
1 голос
/ 09 октября 2011

Я пишу тесты для переводчика с некоторого языка программирования в Java с использованием фреймворка JUnit. Для этого я создал большое количество тестовых случаев, большинство из которых содержат фрагменты кода на тестируемом языке. Поскольку эти фрагменты обычно невелики, их удобно встраивать в код Java. Однако Java не поддерживает многострочные строковые литералы, что делает фрагменты кода немного неясными из-за escape-последовательностей и необходимости разбивать более длинные строковые литералы, например:

String output = run("let a := 21;\n" +
                    "let b := 21;\n" +
                    "print a + b;");
assertEquals(output, "42");

В идеале я бы хотел что-то вроде:

String output = run("""
    let a := 21;
    let b := 21;
    print a + b;
""");
assertEquals(output, "42");

Одно из возможных решений - переместить фрагменты кода во внешние файлы и сослаться на каждый файл из соответствующего контрольного примера. Однако это добавляет значительную нагрузку на обслуживание.

Другое решение заключается в использовании другого языка JVM, такого как Scala или Jython, который поддерживает многострочные строковые литералы, для написания тестов. Это добавит новую зависимость в проект и потребует портирования существующих тестов.

Есть ли другой способ сохранить ясность фрагментов тестового кода, не добавляя слишком много обслуживания?

Ответы [ 3 ]

1 голос
/ 09 октября 2011

Поскольку вы говорите о других языках JVM, рассматривали ли вы Groovy?Вам нужно будет добавить внешнюю зависимость, но только во время компиляции / тестирования (вам не нужно помещать ее в рабочий пакет), и она предоставляет многострочные строки.И еще одно важное преимущество в вашем случае: его синтаксис обратно совместим с Java (то есть вам не придется переписывать свои тесты)!

1 голос
/ 11 октября 2011

Я делал это в прошлом.Я сделал что-то похожее на то, что было предложено home, я использовал внешний файл (ы), содержащий тесты и их ожидаемые результаты, но используя @Parameterized runner test.

@RunWith(Parameterized.class)
public class ParameterTest {
    @Parameters
    public static List<Object[]> data() {
        List<Object[]> list = new LinkedList<Object[]>();
        for (File file : new File("/temp").listFiles()) {
            list.add(new Object[]{file.getAbsolutePath(), readFile(file)});
        }

        return list;
    }

    private static String readFile(File file) {
        // read file 
        return "file contents";
    }

    private String filename;
    private String contents;

    public ParameterTest(String filename, String contents) {
        this.filename = filename;
        this.contents = contents;
    }

    @Test
    public void test1() {
        // here we test something
    }

    @Test
    public void test2() {
        // here we test something
    }
}

Здесь мы работаем test1() & test2() один раз для каждого файла в / temp, с параметрами имени файла и содержимого файла.Тестовый класс создается и вызывается для каждого элемента, который вы добавляете в список в методе, помеченном @ Parameters.

Используя этот тестовый прогон, вы можете перезапустить определенный файл, если он потерпит неудачу;большинство IDE поддерживают повторный запуск одного неудачного теста.Недостаток @Parameterized заключается в том, что нет никакого способа разумно идентифицировать тесты, чтобы имена появлялись в плагине Eclipse JUnit.Все, что вы получаете, это 0, 1, 2 и т. Д. Но, по крайней мере, вы можете перезапустить проваленные тесты.

Как говорит Хоум, хорошее ведение журнала важно для правильной идентификации сбойных тестов и для помощи в отладке, особенно при работе внеIDE.

1 голос
/ 09 октября 2011

Перенос тестовых примеров в файл работал для меня в прошлом, это был и интерпретатор:

  1. создал файл XML, содержащий фрагменты для интерпретации, а также ожидаемый результат. Это было довольно простое определение XML, список тестовых элементов, в основном содержащий testID, value, expected result, type и description.
  2. реализовал ровно один тест JUnit, который считывал файл и просматривал его содержимое, в случае неудачи мы использовали testID и description для записи неудачных тестов.

В основном это работало, потому что у нас был один общий четко определенный интерфейс для интерпретатора, такой как ваш метод run, поэтому рефакторинг все еще был возможен. В нашем случае это не увеличило затраты на обслуживание, фактически мы могли легко создавать новые тесты, просто добавляя больше элементов в файл XML.

Возможно, это не оптимальный способ использования модульных тестов, но он хорошо сработал для нас.

...