Airports Web Services Project

Хотелось написать полноценный, большой проект c soap запросами JAX-WS и тестированием с комбинаторикой из Junit5, использую хороший Airport web service с множеством методов. Почти удалось его закончить. Но public soap web service (webservicex) перестал работать. Поэтому пусть в этом проекте остается все так, как есть, debug более невозможен.


Использовал:

Spring jdbc Spring context Wsimport Junit 5 Mockito
Jackson JAXB MySQL MAMP Allure2

1. Генерация классов по WSDL.
Использовал стандартную библиотеку wsimport. Удобная, имеет полезные настройки. Скармливаем WSDL файл, на выходе получаем авто-генерированные классы для работы с soap сервисом.

Используя консоль выполнить команду:

wsimport -keep -verbose http://gotoqa.ru/ws/server?wsdl
Или
wsimport -d “C:\Users\Documents\Airport web service\” -keep -verbose http://gotoqa.ru/ws/server?wsdl

2. Сборка проекта. Intellij idea – Maven. Настройка pom.xml

Создаем новый maven проект.

Добавляем необходимые зависимости в pom.xml. Ссылка на проект Git будет в конце статьи.

Добавляем конфигурационные файлы в resources проекта.

allure.properties – Настройка для отчетов Allure;
application-config.xml – Описание bean для spring поднятия контекста.
Указываем ссылку на сгенированные классы – сервис, порт, указываем WSDL. Ключевой момент, в дальнейшем по этому application-config создается ApplicationContext.

1
2
3
4
5
     <bean id="customerService"
          class="ru.testqa.modelsairports.Airport"/>
 
    <bean id="port" class="ru.testqa.modelsairports.Airport"
          factory-bean="customerService" factory-method="getAirportSoap"/>

db.xml – Настройка bean (dataSource) соединения с БД.
Прописываем драйвер для подключения. У MySQL дефект был с часовой зоной, serverTimezone=UTC.
А моем примере MySQL, для Oracle будут другие property name=”url” и value.

1
2
3
4
5
6
7
8
 <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <!--fix UTC defect ?serverTimezone=UTC-->
        <property name="url" value="jdbc:mysql://localhost:3306/airports?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

log4j2.xml – Настройка логирования log4j2.xml

3. Поднятие БД MySQL. Spring Jdbc. MAMP.
Для поднятия MySQL сервера локально, без танцев с бубном, использую мега software MAMP. Есть версия для Win и для OS X Сайт разработчиков.
Небольшой фикс для дефекта на маках, после перезапуска не всегда стартует БД сервер, надо удалить ib_logfile файлы.
rm /Applications/MAMP/db/mysql/ib_logfile* #(or wherever your MAMP is installed)
На win os такого дефекта не замечал. Итак, сервер БД установлен и стартован.

Эти настройки прописываем в db.xml.
Создадим небольшую таблицу и наполним ее, использовать будем для написания автоматизированных тестов.

Сначала создадим БД. Используя phpMyAdmin встроенный в MAMP.

Для работы с MySQL я использую стандартные средства, встроенные в Intellij idea – Database.

Создадим таблицу и наполним тестовыми данными.

 -- Create TABLE
CREATE TABLE WorldAirports (
  id INT(11) NOT NULL,
  AirportCode VARCHAR(30) NOT NULL,
  CityOrAirportName VARCHAR(30) NOT NULL,
  Country VARCHAR(30) NOT NULL,
  CountryAbbrviation VARCHAR(10) NOT NULL,
  CountryCode INT(11) NOT NULL,
  GMTOffset INT(11) NOT NULL,
  RunwayLengthFeet INT(11) NOT NULL,
  RunwayElevationFeet INT(11) NOT NULL,
  LatitudeDegree INT(11) NOT NULL,
  LatitudeMinute INT(11) NOT NULL,
  LatitudeSecond INT(11) NOT NULL,
  LatitudeNpeerS VARCHAR(5) NOT NULL,
  LongitudeDegree INT(11) NOT NULL,
  LongitudeMinute INT(11) NOT NULL,
  LongitudeSeconds INT(11) NOT NULL,
  LongitudeEperW VARCHAR(5) NOT NULL,
  PRIMARY KEY (id)
);
 
SELECT * FROM WorldAirports;
 
-- Insert data
INSERT INTO WorldAirports VALUES
  (1, 'LAX', 'LOS ANGELES INTL', 'United States', 'US', 91, 8, 12091, 126, 33, 56, 0, 'N', 118, 24, 0, 'W'),
  (2, 'FCO', 'ROME DA VINCI', 'Italy', 'IT', 450, -1, 12795, 14, 41, 47,59, 'N', 12, 14, 3, 'E'),
  (3, 'SFO', 'SAN FRANCISCO INTL', 'United States', 'US', 91, 8, 11870, 12, 37, 37, 0, 'N', 122, 23, 0, 'W');

Example with NamedParameterJdbcTemplate.

public class DbNamedParametrJdbc {
 
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("db.xml");
        HashMap String, Object paramMap = new HashMap();
        paramMap.put("AIRPORTCODE", "LAX");
        List newDatasetAirs = new NamedParameterJdbcTemplate
                (context.getBean(DataSource.class)).query("SELECT * FROM WORLDAIRPORTS WA WHERE WA.AIRPORTCODE = :AIRPORTCODE",
                paramMap, new BeanPropertyRowMapper(AirportDb.class));
        System.out.println(newDatasetAirs);
    }
}

Проверка DELETE.

public class DbNamedParametrUpdateJdbc {
 
    @Test
     void main1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("db.xml");
        HashMap String, Object paramMap = new HashMap();
        paramMap.put("ID", 4);
        int count = new NamedParameterJdbcTemplate
                (context.getBean(DataSource.class)).update("DELETE FROM WorldAirports WHERE ID = :ID",
                paramMap);
        System.out.println(count);
    }
}

4. SOAP request, get Response. Unmarshal JAXB. Annotation Jackson.

public class LaxtRqDbSpringTest {
 
    private static ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"application-config.xml"});
    private static Airport bean = context.getBean(Airport.class);
    private static ClassPathXmlApplicationContext contextdb = new ClassPathXmlApplicationContext("db.xml");
    private static JdbcTemplate jdbc = new JdbcTemplate(contextdb.getBean(DataSource.class));
    private static List newDatasetAirs;
    private static NewDatasetAir unmarshal;
    private static NewDatasetAir.Table response;
    private static String getAirResp;
    private static AirportDb selectDb;
 
    @BeforeAll
    public static void getResponse(){
        getAirResp = bean.getAirportSoap().getAirportInformationByAirportCode(AIRCODE);
        unmarshal = JAXB.unmarshal(new StringReader(getAirResp), NewDatasetAir.class);
        response = unmarshal.getNewDataSets().get(0);
 
        NamedParameterJdbcTemplate nqu = new NamedParameterJdbcTemplate(contextdb.getBean(DataSource.class));
        HashMap String, Object param = new HashMap();
        param.put("AirportCode", AIRCODE);
        List newDatasetAirs = nqu.query("SELECT * FROM WorldAirports WHERE AirportCode = :AirportCode",
                param,
                new BeanPropertyRowMapper(AirportDb.class));
 
        //newDatasetAirs = jdbc.query(QueryStore.getAirDataDb(), new BeanPropertyRowMapper(AirportDb.class), AIRCODE);
        selectDb = newDatasetAirs.get(0);
    }
 
    @Test
    @DisplayName("ID:24 AirportInformationByAirportCode Response positive test. AirportCode")
    @Description("Test AirportInformationByAirportCode Request and Response. Assertion response web service with data base. (Spring)")
    public  void responseConstTest24(){
        Assertions.assertEquals(selectDb.getAirportCode(), response.getAirportCode(), "Incorrect AirportCode value in response.");
    }
}

ApplicationContext context – создаем context,
getAirResp = bean.getAirportSoap().getAirportInformationByAirportCode(AIRCODE); – SOAP Запрос / Ответ.
Полученный объект необходимо размаршалить с JAXB, заранее описанным классом NewDatasetAir.class, с использованием аннотаций Jackson.
unmarshal = JAXB.unmarshal(new StringReader(getAirResp), NewDatasetAir.class);
response.getAirportCode() – Теперь можем получать любой объект XML из ответа.

@XmlRootElement(name="NewDataSet")
@XmlAccessorType(XmlAccessType.NONE)
public class NewDatasetAir {
    @XmlElement(name = "Table")
    @JsonProperty("Table")
    @JacksonXmlProperty(localName = "Table")
    private List

newDataSets; @XmlType @XmlAccessorType(XmlAccessType.NONE) public static class Table { @XmlElement(name = “AirportCode”) @JsonProperty(“AirportCode”) @JacksonXmlProperty(localName = “AirportCode”) private String airportCode; @XmlElement(name = “CityOrAirportName”) @JsonProperty(“CityOrAirportName”) @JacksonXmlProperty(localName = “CityOrAirportName”) private String cityOrAirportName;

Использование JAXB для размаршаливания XML является хорошей практикой. Позволяет потом удобно работать с элементами XML. Если в ответе будет прилетать JSON, то и это предусмотрено в аннотациях от Jackson. В данном примере использовал для размаршаливания описанный заранее класс NewDatasetAir с использованием Jackson. Бывает более удачно описанные WSDL (Web Services Description Language) и методы которые генерят классы по WSDL. В таких случаях не приходится описывать самостоятельно XML.

5. Junit5 – @ParameterizedTest. Mockito
Uses ParameterizedTest annotation in Junit5 for our tests, also costomized display test name. Run all tests set for 3 airports.

@ParameterizedTest(name = "{index} =&gt; Testing airport: ''{0}''")
    @DisplayName("Test Suite Airports")
    @ValueSource(strings = { "LAX", "FCO", "SFO" })
    void getResponse(String aiport){

В данном проекте не акцентировал внимания на инструменте мокирования. Но пример как мокировать web service сделал. Библиотека – Mockito. Позднее ей будет посвящен отдельный проект. Но в примере видно, описываем мокированный ответ, при поступлении запроса на сервис с любым строковым значением отдавать в ответе переменную TEST, в переменную загнал XML.

public class LaxtRqJavaMock {
    private NewDatasetAir unmarshal;
    private NewDatasetAir.Table response;
    private AirportSoap airportSoap;
 
    @BeforeEach
    public void getResponse(){
        this.airportSoap = mock(AirportSoap.class);
        when(airportSoap.getAirportInformationByAirportCode(anyString()))
                .thenReturn(TEST);
    }
    @Test
    @DisplayName("AirportInformationByAirportCode Response positive test.")
    @Description("Test AirportInformationByAirportCode Request and Response. Assertion response web service with constant data. (Java)")
    public  void responseConstTest(){
        Assertions.assertEquals(TEST, airportSoap.getAirportInformationByAirportCode("123"));}}

6. Create Test report in Allure2
Ну и итог проекта и тестирования, хорошо продуманный отчет с библиотекой Allure2 от Yandex.

allure serve D:\JAVA\Java_SRC\AirportWebServices\target\allure-results

 

 

Attached files:
Test Cases Airports Project
Github AirportsProject

Releated Post