Посоветуйте, как тестить парсер DOM, SAX ...

Есть XML, я её распарсил несколькими способами. Далее необходимо написать к парсерам модульные тесты.

public class ParserDOM {
    public static void main(String[] args) throws Exception {
        InputStream is = new FileInputStream("c:/myXML.xml");
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setNamespaceAware(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(is);
        doc.getDocumentElement().normalize();
        System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
        NodeList nList = doc.getElementsByTagName("CD");
        for (int temp = 0; temp < nList.getLength(); temp++) {
            Node node = nList.item(temp);
            System.out.println("\nCurrent Element :" + node.getNodeName());
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element eElement = (Element) node;
                System.out.println(" TITLE : " + eElement.getElementsByTagName("TITLE").item(0).getTextContent());
                System.out.println(" ARTIST : " + eElement.getElementsByTagName("ARTIST").item(0).getTextContent());
                System.out.println(" COUNTRY : " + eElement.getElementsByTagName("COUNTRY").item(0).getTextContent());
                System.out.println(" COMPANY : " + eElement.getElementsByTagName("COMPANY").item(0).getTextContent());
                System.out.println(" PRICE : " + eElement.getElementsByTagName("PRICE").item(0).getTextContent());
                System.out.println(" YEAR : " + eElement.getElementsByTagName("YEAR").item(0).getTextContent());
                System.out.println();
            }
        }
    }
}

Ребята посоветуйте какую то литературу, ссылки, либо может есть у кого то пару примеров. К чему именно необходимо писать модульные тесты в данном классе(инициализация объектов, или только к вызовам обьекта doc)

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

громко назвать это «XML парсером». Скорее, узкозаточенный парсер вполне опредленной конфигурации.
касательно тестов, кроме того, что уже подсказывали(распарсить, сгенерировать обратно и сравнить), предлагаю еще добавить тест на не валидные данные. начиная от глобальных проблем(файл не найден) и заканчивая неполными данными.
вон, у тебя в классе написано throws Exception. для чего, как думаешь?
вообще, идеальна для тестирования функция, которая все данные берет в параметрах конструктора/методов.
то есть, не дожно быть захардкодженного

InputStream is = new FileInputStream("c:/myXML.xml");
внутри.
а должен быть параметр(какого типа?), который предоставить коду класса доступ к данным.

спасибо за совет. throws Exception — предупреждаю о куче exceptions (IOException, ParserConfigurationException, SAXException) которые могут вылететь с метода main

ага. а в каких случаях те самые Exception вылетают?
я к чему веду — эти ситуации тоже надо тестировать.

Поменьше слушайте тех, кто не может написать, а только ругается, они просто потеряли навыки. Пишется примерно так:
Вот наша XML-ка лежащая по нужному пути:

<SomeRootElement>
        <CD id="1">
            <TITLE>Rhapsody of fire</TITLE>
			<ARTIST>Luca Turilli</ARTIST>
			<COUNTRY>Italy</COUNTRY>
			<COMPANY>Producer Valentin(c)</COMPANY>
			<PRICE>19.99$</PRICE>
			<YEAR>2002</YEAR>
        </CD>
</SomeRootElement>
Бин, который содержит данные и парсер их запихивает в этот бин. Кстати геттеры/сеттеры не для прикола нужны, а в них можно что-то полезное засунуть. Equals нужно, т.к. тестовый класс сравнивает оба объекта по equals, hashcode переопределять вместе — good practice
package org.dou.lalala;

/**
 * Date: 6/18/14
 * Time: 4:21 PM
 */
public class Song {

    private String title;
    private String artist;
    private String country;
    private String company;
    private String price;
    private String year;

    public Song() {}

    public Song(String title, String artist, String country, String company, String price, String year) {
        this.title = title;
        this.artist = artist;
        this.country = country;
        this.company = company;
        this.price = price;
        this.year = year;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        checkForEmpty(title);
        this.title = title;
    }

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        checkForEmpty(artist);
        this.artist = artist;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        checkForEmpty(country);
        this.country = country;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        checkForEmpty(company);
        this.company = company;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        checkForEmpty(price);
        this.price = price;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        checkForEmpty(year);
        this.year = year;
    }

    private void checkForEmpty(String value) {
        if (value == null || value.length() == 0) {
            throw new IllegalArgumentException("Value shouldn't be empty");
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Song song = (Song) o;

        if (artist != null ? !artist.equals(song.artist) : song.artist != null) return false;
        if (company != null ? !company.equals(song.company) : song.company != null) return false;
        if (country != null ? !country.equals(song.country) : song.country != null) return false;
        if (price != null ? !price.equals(song.price) : song.price != null) return false;
        if (title != null ? !title.equals(song.title) : song.title != null) return false;
        if (year != null ? !year.equals(song.year) : song.year != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = title != null ? title.hashCode() : 0;
        result = 31 * result + (artist != null ? artist.hashCode() : 0);
        result = 31 * result + (country != null ? country.hashCode() : 0);
        result = 31 * result + (company != null ? company.hashCode() : 0);
        result = 31 * result + (price != null ? price.hashCode() : 0);
        result = 31 * result + (year != null ? year.hashCode() : 0);
        return result;
    }
}

Сам парсер, который парсит в бин. Если нужно много песен из XML-ки брать, есть коммент

package org.dou.lalala;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;


/**
 * Date: 6/18/14 Time: 4:00 PM
 */
public class CustomDomParser {

    public static Song parseXMLToSong(String path) throws Exception {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        Document doc = docBuilder.parse(path);

        //change this piece of code to a loop in case you need to iterate over
        //multiple songs, int this case your should return array or list or set instead
        //of Song bean
        Node cd = doc.getElementsByTagName("CD").item(0);
        NodeList cdAttributes = cd.getChildNodes();
        Song parsedSong = new Song();

        for (int i = 0; i < cdAttributes.getLength(); i++) {
            Node node = cdAttributes.item(i);
            if ("TITLE".equals(node.getNodeName())) {
                parsedSong.setTitle(node.getTextContent());
                continue;
            }
            if ("ARTIST".equals(node.getNodeName())) {
                parsedSong.setArtist(node.getTextContent());
                continue;
            }
            if ("COUNTRY".equals(node.getNodeName())) {
                parsedSong.setCountry(node.getTextContent());
                continue;
            }
            if ("PRICE".equals(node.getNodeName())) {
                parsedSong.setPrice(node.getTextContent());
                continue;
            }
            if ("YEAR".equals(node.getNodeName())) {
                parsedSong.setYear(node.getTextContent());
                continue;
            }
            if ("COMPANY".equals(node.getNodeName())) {
                parsedSong.setCompany(node.getTextContent());
            }

        }
        return parsedSong;
    }

}

тестовый класс, у вас должен быть подключен junit-4.11

package org.dou.lalala;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

/**
 * Date: 6/18/14
 * Time: 4:31 PM
 */
public class CustomDomParserTest {

    @Test
    public void testSongIsNotNullSimple() throws Exception {
        //given
        Song expectedSong = new Song();
        expectedSong.setTitle("Rhapsody of fire");

        //when
        Song parsedSong = CustomDomParser.parseXMLToSong("c:\\myXML.xml");

        //then
        assertNotNull(parsedSong);
        assertEquals(expectedSong.getTitle(), parsedSong.getTitle());
    }

    @Test
    public void testSongIsNotNullComplex() throws Exception {
        //given
        Song expectedSong = constructExpectedSong();

        //when
        Song parsedSong = CustomDomParser.parseXMLToSong("c:\\myXML.xml");

        //then
        assertNotNull(parsedSong);
        assertEquals(expectedSong, parsedSong);
        /*assertEquals(expectedSong.getTitle(), parsedSong.getTitle());
        assertEquals(expectedSong.getCompany(), parsedSong.getCompany());
        assertEquals(expectedSong.getCountry(), parsedSong.getCountry());
        assertEquals(expectedSong.getPrice(), parsedSong.getPrice());
        assertEquals(expectedSong.getArtist(), parsedSong.getArtist());
        assertEquals(expectedSong.getYear(), parsedSong.getYear());*/
    }

    private Song constructExpectedSong() {
        return new Song("Rhapsody of fire", "Luca Turilli", "Italy", "Producer Valentin(c)",
                "19.99$", "2002");
    }

}

Итого 39 минут на всё про всё.
Тупанул с equals и дебажил минут 10. Всегда внимательно относитесь к нему и к hashcode еще внимательней.
Будут вопросы, пожелания — пишите прямо на форуме.
Удачи.

Коментар порушує правила спільноти і видалений модераторами.

Пишется примерно так:
А теперь сядьте и подумайте почему «*-непрофессионализм — главный тренд отрасли». :)

Ваши претензии без действующего кода с вашей стороны = пук в лужу.

зачем вы написали готовое решение к учебному заданию?
upd дочитал до конца: так что тестовому, а не учебному.

Женя, в этом решении все равно автору придется разобраться до собеседования, так что ничего плохого вроде как нет.

какая разница между самостоятельным поиском решения и чтением готового ответа?
вы н замечали, что уже решенные проблемы кажутся простыми?

вы н замечали, что уже решенные проблемы кажутся простыми?

Да, Женя, также, до Prom’а, я часто замечала, когда на мои вопросы, начинающего инженера, часто ответы были разбросаны по разным контекстам, которые я никак не могла слепить в кучу у себя в голове, и чувствовала себя полной дурочкой. А если бы было решение, даже пусть не на 100% покрывающее проблему, было бы гораздо легче, так как сама бы выбирала свой путь в изучении материала. Вообщем-то это и стало причиной того, что я поддержала хороший поступок, как я считаю, Gabriel’а.

Огромная Вам благодарность! Спасибо))

Всегда рад, будут вопросы, проблемы, пожелания — пишите сразу на форуме. Мне в ученичестве помочь было некому, были лишь Петросяны кругом.

Петросяны уже давно завели своих Степаненко и размножаются... страшно представить что будет дальше.

А мы вчера на курсах проходили паттерн Interpretator. ( Это подсказка по теме... )

Паша, возможно и не сознательно, но помойму Вы ввели человека в заблуждение. Либо пролейте пожалуйста свет на Ваше высказывание.

Используя данный патерн с regex можно написать парсер(свой а не встроенный). Тестирование будет заключается в 3-4 строки. Да мой совет более самому парсеру а не тестированию подходит. Но даже я как джун вижу что в мейне столько кода не должно быть.

Ребята посоветуйте какую то литературу, ссылки, либо может есть у кого то пару примеров.
Где-то тут dou.ua/.../java-digest-1 есть ответ.
.
P.S. Но судя то вашему «паресеру», вам стоит начать с чего-то вроде «основы программирования».

спс, а в чем недостатки парсера? подскажите, что неверно, буду благодарен.

спс, а в чем недостатки парсера?
Например, у меня на машине нет файла «c:/myXML.xml». А книгу по основам программирования вы таки почитайте :)

Неконфигурируемый, что усложняет тестирование; как для парсера писать сообщения напрямую в system.out плохой подход (лучше использовать интерфейс logger фреймворка. Почему — в интернете много информации на этот счет). Это то, что бросилось мне в глаза сразу.

Если уж настолько примитивно, то хотя бы научись перебирать дерево циклом по Node.getNextSibling()

И таким нехитрым способом ты можешь перебирать мелкие XML-ки. Для крупного — ты многократно (!!!) просматриваешь одни и те же узлы. А потому первое что делают при парсинге XML — это парсят его в нормальный класс, в Properties или ещё какую удобную для програмирования структуру. Либо если это какая-то типичная XML-структура более высокоуровневого протокола (тот же самый SOAP) — то там есть свои парсеры, возвращающие честный класс.

Тестить то что ты написал — мягко скажем сложновато. Если ты всё выбрасываешь в System.out, то где ты собираешься искать выходные данные для теста?

Ещё более железобетонный вариант — положить ПРИМЕР XML прямо в проект. Это поможет разработчику увидеть что от него ждут, это поможет при передаче проекта другому разработчику, и конечно же этот пример будет отдан вместе с документацией автору кода, который собственно и будет посылать XML.
И тот же файл будет лететь на автоматический тест. И из него же будут делать копию для ручного теста ежели таковой понадобится.
Самый простой пример крупнокалиберного теста — это забилдить XML обратно из распарсенного класса, и убедиться что в нём всё есть. Можно запустить полученный XML обратно на парсер, и убедиться что результат идетничен с точностью до байта [контрольной суммы] или проверить более сложным текстовым анализом где ошибка.
Более правильно — предоставить этот тест на публичный интерфейс в продакшен. Чтобы разработчик фронтенда мог со своей стороны его запустить, и найти когда разработчик серверной части что-то накосячил с конфигом. Это позволяет снизить требования к опыту разраба фронтенда, то бишь быстро отлавливать типовые ашыпки.

Тестовое задание что ли?

И он его по ходу уже провалил. Угадай с одного раза, те кто ему задание дал — не сидят ли на DOU?

Я бы на их месте дал ему сейчас и решение, но с какой-нить уникальной заковыкой в коде. И кто этот код принесёт — попадает в чёрный-чёрный список.

Мораль: если уж собирается читить — заказал бы на фрилансе. И то же самое когда уже устроился на работу: что-то не получается — дал доллар индусу и вуаля. А так... спалился поцаньчик :)

Во первых: я не просил, что бы задание решили за меня, я лишь попросил литературу, ссылки, примеры....Что вы видите в этом плохого???
Во вторых: Компания прекрасно знает, что я пока не знаю данную тему, и буду её учить.
Так что не*рена я не спалился, поцаньчик)))

а спросить на форуме это не по-пацански?

На форуме где работодатель сидит? Ещё б в саппорт компании написал :)

Підписатись на коментарі