<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Юпи! - CMS на Yiiframework!</title>
<link href="https://yupe.ru:443"></link>
<id>urn:uuid:03c2c8cd-2de1-771e-f552-b0bd7fcbaf4c</id>
<description type="html"><![CDATA[Юпи! - Сообщество разработчиков на Yiiframework и Юпи! CMS
]]></description>
<updated>2020-06-30T22:39:29+03:00</updated>
<entry>
<title>ЧТО ТАКОЕ КОНВЕРГЕНТНЫЕ БАЗЫ ДАННЫХ?</title>
<author>xoma</author>
<published>2020-06-05T00:00:00+03:00</published>
<updated>2020-06-05T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>Если вам нужно создать мобильное приложение для обмена документами между пользователями, потребуется база данных, специально предназначенная для хранения документов. А для проекта, где нужна платежная система с защитой от мошенничества, и также нужно хранить табличные данные, потребуется сразу две базы данных, блокчейн и реляционная база данных.
</p><p><img src="https://yupe.ru/uploads/image/0dc7bbc110c58b31635f70e89f5a48ef.png">
</p><p>Необходимость распределять свои ресурсы среди всех этих узкоспециализированных хранилищ данных может вызвать у вас чувство тревоги. Но, возможно, решение вашей проблемы – не увеличение ресурсов, а единая конвергентная база данных.
</p><p>Единая конвергентная база данных – это база данных с поддержкой всех современных типов данных и новейших парадигм разработки.
</p><p>Конвергентные базы данных поддерживают хранение геоданных, JSON для хранения документов, IoT для объединения устройств в единую сеть, размещение данных в оперативной памяти (in-memory технологии) для аналитики в реальном времени и, конечно же, традиционные реляционные данные. Обеспечивая поддержку всех этих типов данных, конвергентная база данных может выполнять все виды нагрузок (от IoT, блокчейна, аналитики и машинного обучения). Она также поддерживает любые парадигмы разработки, включая микросервисы, события, REST, SaaS, CI/CD и др.
</p><p>Традиционно, когда появляются новые технологии управления данными, они реализуются как отдельные продукты. Например, когда блокчейн впервые вышел на рынок, это была отдельная автономная система, которая требовала использования совершенно иного, специфического способа хранения и доступа к данным.
</p><p>Объединение новых типов данных, нагрузок и парадигм в рамках единой базы данных, значительно упрощает поддержку смешанных нагрузок и типов данных. Вам не нужно обслуживать несколько систем или беспокоиться о необходимости обеспечения единой безопасности для всех этих систем.
</p><p>Вы также получаете эффективное взаимодействие этих возможностей между собой. Например, за счет поддержки алгоритмов машинного обучения и пространственных данных в одной базе данных, вы можете легко проводить прогнозную аналитику на этих данных. Это заметно упрощает и ускоряет разработку приложений, управляемых данными (data-driven).
</p><p>Хорошей аналогией конвергентной базы данных является смартфон. Раньше, если вам нужно было позвонить, вы использовали телефон, а если хотели сделать снимок или видео – камеру. Если же вы хотели куда-нибудь отправиться, вам нужна была карта или навигационная система. Для прослушивания музыки, вам нужен был iPod или другое аналогичное устройство. Смартфон сочетает в себе все эти функции, что существенно облегчает вашу жизнь.
</p><p>Схожая идея простоты и удобства воплощена в конвергентной базе данных.
</p><p>Oracle Database является отличным примером конвергентной базы данных, поскольку в ней обеспечивается поддержка машинного обучения, блокчейна, графовых баз данных, пространственных данных, JSON, REST, событий, версионирования и потоковых данных IoT (всё это не требует дополнительной оплаты). Это позволяет поддерживать множество разнообразных проектов на единой платформе, значительно снижая сложность и расходы на управление, при этом минимизируя риски.
</p><p>____________________________________________________________
</p><p>Является ли конвергентная база данных просто мультимодельной базой данных? В последнее время многие базы данных, добавив поддержку еще одного типа данных или нагрузки, начали позиционироваться как мультимодельные, т.к. они уже не специализируются на чем-то одном. Конвергентная база данных выделяется тем, что она объединяет не несколько, а все виды нагрузок и все типы данных, которые используют современные приложения, управляемые данными. Кроме того, конвергентная база данных имеет возможность гибко масштабироваться, чтобы справляться с любыми нагрузками.
</p><p>В чем разница между конвергентной базой данных и Oracle Autonomous Database? Автономная база данных – это семейство облачных сервисов, использующих машинное обучение для автоматизации рутинных задач управления базами данных. Конвергентная база данных является одним из компонентов, составляющих автономную базу данных Oracle.
</p><p><img src="https://yupe.ru/uploads/image/8e66f107f1c75392241eac5df244af9b.png">
</p><p>Начинать работу с конвергентной базой данных очень дорого? Внедрение облачных баз данных позволило заметно снизить цены. В долгосрочной перспективе организации экономят время и деньги, т.к. требуется меньше аппаратного и программного обеспечения, при меньшем количестве оборудования эксплуатационные расходы также снижаются.
</p><p>Есть ли случаи, когда узкоспециализированная база данных является более выгодным выбором? Использование таких баз данных должно быть ограничено небольшим набором весьма специфических задач. Например, торговые системы, для них критично время ожидания, необходимо гарантировать время отклика в микросекундах, такие приложения выиграют от запуска на Oracle TimesTen.
</p><p><br>
</p><p><br></p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/QnTzm9SShBs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>Источник - <a href="https://blogs.oracle.com/database/what-is-a-converged-database">https://blogs.oracle.com/database/what-is-a-conver...</a>
</p><p><br>
</p>]]></summary>
<link href="https://yupe.ru:443/post/chto-takoe-konvergentnye-bazy-dannyh.html"></link>
<id>urn:uuid:4dc00acb-5a12-c78d-7fac-68f3b9eb2f2a</id>
</entry>
<entry>
<title>Бизнес-модель Symfony</title>
<author>xoma</author>
<published>2020-05-29T00:00:00+03:00</published>
<updated>2020-05-29T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>Фабьен Потенсьер работает над Symfony уже более 15 лет и в одном из постов своего блога он рассказал, почему Symfony нуждается в устойчивой бизнес-модели и чего стоит ожидать Symfony сообществу.</p><p>“Популярность и использование Symfony продолжает расти. Компоненты Symfony были скачаны более 2-х миллиардов раз, десятки значимых PHP проектов построены на основе Symfony, тысячи людей посещают Symfony конференции. Я люблю код, но код не представляет никакой ценности без активного сообщества. За эти годы общими усилиями мы создали потрясающее сообщество, и я очень горжусь тем, чего мы вместе достигли.</p><p>Все это никогда не было бы возможным без помощи моего другого детища, компании SensioLabs, которая поддерживает Symfony с первого дня. Первая Symfony конференция была профинансирована SensioLabs. Компания обеспечивает зарплату мне и некоторым другим участникам проекта, оплачивает персональный сайт symfony.com, ответвленные проекты, спонсирует некоторые митапы. Сегодня симбиоз между SensioLabs и Symfony силен как никогда. Компания продвигает идеи Symfony и готова оплачивать работу над Symfony, потому что это взаимовыгодно и помогает SensioLabs находить новых клиентов и новые возможности.  </p><p>Но мне бы хотелось большего, хотелось бы вывести Symfony на новый уровень, поэтому я принял решение о переходе к следующему шагу ­– поиску устойчивой бизнес-модели для Symfony. Для некоторых это может стать неожиданностью, но никто не может работать полный рабочий день бесплатно в течение стольких лет. Некоторые опенсорс проекты можно поддерживать как хобби. Twig можно было бы отнести к таким проектам, но не Symfony. У Symfony сотни тысяч пользователей, сумасшедшая ежедневная активность и множество зависимых проектов. В любом случае, большинство успешных опенсорс проектов имеют какую-то бизнес-модель.</p><p>В январе 2018 я основал независимую коммерческую компанию Symfony SAS, которая теперь отвечает за проект Symfony. Я и мой давний партнер Грегори Паскаль – главные акционеры. Я также руковожу дальнейшим курсом развития. Команда, которая 100% своего времени посвящает работе над Symfony и раньше работала в SensioLab, теперь работает на компанию Symfony. Мне очень повезло, что у меня есть возможность работать с такими потрясающими людьми.</p><p>В настоящее время основной доход приходит от SymfonyInsight, который занимается аналитикой Symfony проектов и предоставляет эффективные рекомендации по их улучшению. Конференции и сертификация – это еще два платных продукта от компании Symfony, но они не влияют на чистую прибыль компании.</p><p>Хорошая новость в том, что у нас есть запас времени, чтобы определиться с оптимальной бизнес-моделью для Symfony. Когда Symfony отделилась от SensioLabs, мы выделили Symfony финансирование как минимум на 3 года. SensioLabs по-прежнему активно спонсирует проект и планирует делать это еще долгие годы.</p><p>Прежде чем говорить о будущем, давайте проясним один момент. Мы не планируем создавать платную версию Symfony, фреймворк и его компоненты навсегда останутся бесплатными. Мы сосредоточимся на других вариантах.</p><p>Мы создали простой, но мощный инструмент Symfony Security Monitoring. Это удобный способ получать уведомления о найденных уязвимостях в ваших проектах (даже если вы уже не работает активно над проектом). Вы сами выбираете цену, которую готовы платить за данный функционал. Вы не просто донатите, вы получаете что-то полезное взамен.</p><p>Мы много думали о том, какие инструменты нужны Symfony, и пришли к выводу, что мы хотим создать полезный инструмент для Symfony разработчиков, который бы упрощал работу над проектом, начиная от его создания и до деплоя в продакшн. Сложность проектов растет, как и число зависимостей, современные проекты нуждаются в конвейере сборки (Encore), обмене сообщениями (Symfony Messenger) и других полезных утилитах. В качестве решения мы создали SymfonyCloud, в паре с Symfony CLI вы получаете всё необходимое для работы над Symfony проектами.</p><p><a href="https://symfony.com/cloud/" target="_blank">SymfonyCloud</a> и <a href="https://symfony.com/download" target="_blank">Symfony CLI</a> являются частью новой бизнес-модели Symfony.</p><p>В моих планах сделать Symfony самодостаточным проектом. Я думаю, что мы на правильном пути и нас ждет захватывающее будущее!”</p>]]></summary>
<link href="https://yupe.ru:443/post/biznes-model-symfony.html"></link>
<id>urn:uuid:95e9a996-ce11-4a29-afc9-218802ddfd95</id>
</entry>
<entry>
<title>Книга о Symfony 5</title>
<author>xoma</author>
<published>2019-11-13T00:00:00+03:00</published>
<updated>2019-11-13T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>Фабьен (создатель symfony) запустил кампанию на кикстартере по сбору средств для выпуска новой книги о Symfony 5.</p><p>Судя по содержанию <a href="https://www.kickstarter.com/projects/fabpot/symfony-5-the-fast-track/description"></a><a href="https://www.kickstarter.com/projects/fabpot/symfony-5-the-fast-track/posts/2676649">https://www.kickstarter.com/projects/fabpot/symfon...</a> , книжка будет просто замечательной.</p><p>Работа с git, docker, http api, twig, тестирование, postgresql, асинхронность и RabbitMq, кеширование, создание SPA-приложения и еще много много всего!</p>]]></summary>
<link href="https://yupe.ru:443/post/kniga-o-symfony-5.html"></link>
<id>urn:uuid:48ee42d3-3223-4eae-2cb8-0d0a6d6eb9dc</id>
</entry>
<entry>
<title>Должен ли бекендер знать SQL</title>
<author>xoma</author>
<published>2019-10-02T00:00:00+03:00</published>
<updated>2019-10-02T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>В фейсбуке развернулась интересная дискуссия на тему "Должен ли бекендер знать SQL", приводим выжимку и самые яркие цитаты =)</p><p><br></p><p><em>Тарантул - это сбитый лётчик. Чемодан без ручки. Гайка с семью гранями. Пятое колесо. Шестой палец на ноге. </em></p><p><em>Могу продолжить.</em><em></em><br></p><p><em><br></em></p><p><em>А это кстати весьма распространенная история среди джавистов. <br>Я пока не начал плотно работать с джавой, даже не предполагал, что на собеседования будут приходить люди на сениорские позиции серверной разработки без знания sql.<br>Причем у людей может быть 10+ лет коммерческой разработки, в которой им ни разу не потребовалось left join написать.<br>Ну и кстати, когда у тебя есть состояние между запросами, то необходимость в оптимальных запросах уходит на второй план <span class="_5mfr"><span class="_6qdm">:)</span></span></em></p><p><span class="_5mfr"><span class="_6qdm"><em></em></span></span><em>С линуксами как "технологией" кстати аналогичная ситуация: есть вполне успешные разработчики, которые не умеют в cd, mkdir и rm -rf /*</em></p><p><em><br></em></p><p><em>Не знать sql бекендеру позволительно только в одном случае: если это junior в самом начале пути.<br>В остальных случаях это или битриксоид в худшем смысле или случайный пассажир<br></em></p><p><em><br></em></p><p><em>Ну так за них бездушная машина всё делает же. Для большинства задач хватает.<br></em></p><p><em><br></em></p><p><em>Я недавно собеседовал фронт-разработчиков на vue. Открыл мануал, следующую страницу после Introduction, в ней 2 раздела посвящены Lifecycle приложения. Я просил кандидатов рассказать о жизненном цикле приложения на vue. Из десяти девять не понимали вопрос. 90% соискателей вообще никогда не смотрели документацию. Какой SQL?<span class="redactor-invisible-space"><br></span></em></p><p><em><span class="redactor-invisible-space"><br></span></em></p><p><em><span class="redactor-invisible-space">Со знанием sql последние лет 10 действительно у кандидатов деградация. Все сидят за фреймворками, и самоучкам программирования нет мотивации фундаментально изучать эту науку. И видимо на фоне деградации фундаментального образования индустрии разработки.<span class="redactor-invisible-space"><br></span></span></em></p><p><em><span class="redactor-invisible-space"><span class="redactor-invisible-space"><br></span></span></em></p><p><em><span class="redactor-invisible-space"><span class="redactor-invisible-space">Количество компаний, где нагрузка - миллионы чего-то, как раз те самые 10%) Если, компания живет как саас на 1000-4000 клиентов, то там explain не критичен) Хождение по highload-конференциям извращает картину мира)<span class="redactor-invisible-space"><br></span></span></span></em></p><p><span class="redactor-invisible-space" "=""><span class="redactor-invisible-space" "=""><span class="redactor-invisible-space" "=""><span class="redactor-invisible-space" "=""><br></span></span></span></span></p><p><em>Если человек отлично финтит техническими терминами и теорией, это совсем не значит что он сможет адекватно делать бизнес-задачи. Обжигались очень больно.</em></p><p><em>И на оборот, если человек плавает в тех части, но ход мыслей верный - может быть все хорошо.</em></p><p><em><br></em></p><p><em><strong>Джойн не масштабируется;))) поэтому все тру спецы по хайлоадам не умеют джойн;)</strong><strong></strong><br></em></p>]]></summary>
<link href="https://yupe.ru:443/post/dolzhen-li-bekender-znat-sql.html"></link>
<id>urn:uuid:840d6a3a-b67d-d893-c739-791978ed861f</id>
</entry>
<entry>
<title>Symfony 5 уже в ноябре</title>
<author>xoma</author>
<published>2019-09-28T00:00:00+03:00</published>
<updated>2019-09-28T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>Классная презентация об истории возникновения и развития Symfony.</p><p>Все больше и больше продуктов выбирают этот фреймворк для своей работы: PimCore, Akeneo, Drupal, eZPublish, Laravel, Sylius, ShopWare</p><p>Сюда же можно добавить продукты от Oroinc и даже PrestaShop, который частично перешел на Symfony.</p><p>Релиз Symfony 5 ожидается уже в ноябре! Что войдет:<br></p><ul><li>Стабильные компоненты: <a href="https://symfony.com/doc/master/components/messenger.html" target="_blank">Messenger</a> , <a href="https://symfony.com/doc/current/components/mime.html" target="_blank">Mime</a> и <a href="https://symfony.com/doc/current/components/mailer.html" target="_blank">Mailer</a>, <a href="https://symfony.com/doc/current/components/http_client.html" target="_blank">HttpClient</a></li><li>PHP &gt;= 7.2.9</li><li>Monolog 2 и Twig 3</li><li>Поддержа PHP 7.4 (включая <a href="https://stitcher.io/blog/preloading-in-php-74" target="_blank">прелоадинг</a>)</li></ul><a href="https://symfony.com/doc/current/components/http_client.html" target="_blank"></a><p><a href="https://speakerdeck.com/el_stoffel/symfony-update-on-the-road-to-v5">https://speakerdeck.com/el_stoffel/symfony-update-...</a></p>]]></summary>
<link href="https://yupe.ru:443/post/symfony-5-uzhe-v-noyabre.html"></link>
<id>urn:uuid:c82bf1bf-af49-1f04-3845-ced1d6ab8b99</id>
</entry>
<entry>
<title>Тесты на Codeception для PHP-бэкендов Павел Сташевский, QA-engineer, Lamoda</title>
<author>xoma</author>
<published>2019-03-28T00:00:00+03:00</published>
<updated>2019-03-28T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>Мы очень любим и очень давно используем Codeception для тестирования своих проектов (не всех).
</p>
<p>Когда-то давно мы сделали перевод документации по Codeception на <a href="http://allframeworks.ru/codeception" target="_blank">русский язык</a> (скоро ее обновим).
</p>
<p>Еще нам очень нравится движуха, которую устраивает команда Badoo вокруг PHP и его инфраструктуры.
</p>
<p>Совсем недавно прошел meetup, на котором рассказывали преимущественно про тестирование.
</p>
<p>Своим опытом работы с Codeception поделился Павел Сташевский из Lamoda.
</p>
<p>Обязательно посмотрите его выступление.
</p>
<p>
	<iframe width="560" height="315" src="https://www.youtube.com/embed/l_y4xwHd1b4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
	</iframe>
</p>
<p>А мы сделали текстовую расшифровку доклада.
</p>
<p>Всем привет! Давайте познакомимся. Меня зовут Паша, я — тестировщик, это нормально, все хорошо. Я буду рассказывать про Codeception, про то, как мы его используем в Lamoda, и как на нем, собственно, мы пишем тесты. Кстати, кто пользуется/пользовался, был опыт использования Codeception?
</p>
<p>Ну, есть люди, в общем, это радует. Начнем мы, наверное, как бы немножко с конца, собственно, с PHP-бэкендов, c сервисов, которые мы тестируем с помощью Codeception. В Lamoda много сервисов. Есть сервисы, которые клиентские, которые взаимодействуют непосредственно с нашими пользователями, с пользователями сайта, мобильного приложения. Про них мы говорить не будем. А есть то, что у нас в компании называется глубокие бэкенды, — это наши системы бэк-офиса, который автоматизирует наши бизнес-процессы. Это доставка, это склад, это автоматизация фотостудий, колл-центра. Большинство этих проектов, большинство этих сервисов разрабатывается на PHP, если говорить кратко про стек, это PHP + Symfony, кое-где — старые проекты на Zend’e, в качестве баз данных используется PostgreSQL и MySQL, в качестве систем обмена сообщениями — Rabbit или Kafka.
</p>
<p>Почему это PHP-бэкенды? Потому что у них как правило развесистый API — это либо REST, кое-где есть чуть-чуть SOAP’а. Если у них есть UI, то это UI больше вспомогательный, который используют наши внутренние пользователи.
</p>
<p>ОК. Поговорили немножко про то, что мы тестируем. Начнем с такого простого, легкого вопроса ­— зачем нам нужны автотесты? Не будем дискутировать, я, пожалуй, сразу изложу свою точку зрения, зачем нам в Lamoda автотесты.
</p>
<p>Вообще, когда я пришел работать в Lamoda, там был такой лозунг: “Давайте избавимся от ручного регресса”. Не будем вручную ничего регрессионно тестировать. И мы работали над этой задачей. Собственно, вот одна из главных причин, зачем нам нужны автотесты, — чтобы не гонять регресс руками. А зачем нам это нужно? Правильно, чтобы быстро релизить. Чтобы мы могли безболезненно, очень быстро выкатывать наши релизы и при этом иметь некоторую сетку из автотестов, которые нам будут говорить, хорошо или нехорошо. Это, наверное, самые главные цели. Но есть еще парочка вспомогательных, про которые я тоже хочу сказать.<br>
</p>
<p>Зачем нужны автотесты?<strong></strong>
</p>
<ol>
	<li><strong>Не тестировать регресс руками</strong></li>
	<li><strong>Быстро релизить</strong></li>
	<li><strong>Использовать в качестве документации</strong></li>
	<li><strong>Ускорить onboarding новых сотрудников</strong></li>
</ol>
<p>Первое — автотесты удобно использовать (в некоторых случаях) в качестве документации. Иногда проще зайти в тесты, посмотреть, какие кейсы покрыты, как они работают, и понять, как работает тот или иной функционал, и ускорить вхождение новых сотрудников — как разработчиков, так и тестировщиков, — в новый проект. Когда садишься писать автотесты, сразу становится понятно, как работает система.
</p>
<p>Ок, поговорили о том, зачем нам нужны автотесты. Теперь поговорим о том, какие тесты мы пишем в Lamoda.
</p>
<p><img src="https://yupe.ru/uploads/image/9cf98b2ad990cccd260ed73d53f8e622.png">
</p>
<p>Это достаточно стандартная пирамида тестирования, начиная от unit-тестов, заканчивая E2E-тестами, где тестируются уже некоторые бизнес-цепочки. Про нижние два уровня я говорить не буду, не зря они таким белым цветом закрашены. Это тесты на сам код, их пишут у нас разработчики, тестировщик, в крайнем случае, может зайти в Pull Request, посмотреть код и сказать: “Ну, что-то тут недостаточно кейсов, давайте покроем еще что-нибудь”. На этом работа тестировщика для этих тестов заканчивается. Мы будем говорить про уровни выше, которые у нас пишут и разработчики, и тестировщики. Начнем с системных тестов. Это тесты, которые тестируют API (REST или SOAP), которые тестируют некую внутреннюю логику систем, различные команды, разборы очередей в Rabbit, которые тестируют обмен с какими-то внешними системами. Как правило, эти тесты достаточно атомарные. Они не проверяют какую-то цепочку, они проверяют какое-то одно действие — какой-нибудь один API-метод, какую-то одну команду. И проверяют как можно на большем количестве кейсов, как позитивных, так и негативных. Это тесты достаточно атомарные.
</p>
<p>Идем дальше, E2E-тесты. Я их поделил на 2 части. У нас есть тесты, которые тестируют связку UI и бэкенда. И есть тесты, которые мы называем flow-тесты. Они тестируют цепочку — жизнь объекта от начала до конца. Например, у нас есть система управления процессинга нашими заказами. Внутри такой системы может быть тест: заказ — от создания до доставки, то есть прохождение его по всем статусам. Именно по таким тестам потом очень легко и просто смотреть, как работает система. Вы сразу видите весь flow определенных объектов, с какими внешними системами все это взаимодействует, какие команды для этого используются.
</p>
<p>Ну, про то, что flow-тесты работают иногда как документация, я уже сказал. И еще такой комментарий для UI-тестов. Поскольку у нас этим UI пользуются внутренние наши пользователи, нам не важна кросс-браузерность — мы не гоняем на каких-то фермах эти тесты, нам достаточно проверить в одном браузере, а иногда даже не нужно использовать браузер.
</p>
<p>Окей. “Почему Codeception мы выбрали для автоматизации тестирования?” — наверное, спросите вы. Если честно, у меня нет ответа на этот вопрос. Когда я пришел в Lamoda, Codeception уже был выбран как стандарт, чтобы писать автотесты, и я столкнулся с ним по факту. Но, поработав какое-то время с этим фреймворком, я все-таки понял, почему Codeception. Этим я и хочу с вами поделиться.
</p>
<p>Почему Codeception?<br>
</p>
<p><strong></strong>
</p>
<ol>
	<li><strong>Можно писать и запускать одинаково тесты любых видов (unit, functional, acceptance)</strong></li>
	<li><strong>Многие грабли уе решены, много модулей уже написано</strong></li>
	<li><strong>Во всех проектах, несмотря на немного разные потребности, тесты будут выглядеть одинаково</strong></li>
</ol>
<p>Во-первых, концепция Codeception предполагает, что на этом фреймворке вы пишете любые тесты — unit, интеграционные, функциональные, приемочные.
</p>
<p>И они будут у вас выглядеть ну не совсем, но запускаться, по крайней мере, будут одинаково.
</p>
<p>Во-вторых, Codeception — это достаточно мощный комбайн, в котором уже решено много проблем, много вопросов, много задач для тестов. Если что-то не решено — скорее всего, вы найдете что-то извне — некоторый аддон для какой-то специфической работы. Вам не нужно писать какие-то тестовые обертки для баз данных, для еще чего-то. Просто берете и подключаете к Codeception модули и работаете с ними.Ну и такой плюс (наверное, он больше подходит для больших компаний, когда у вас много проектов и сервисов) — во всех проектах тесты будут выглядеть плюс-минус одинаково. Это очень здорово.
</p>
<p>Кратко скажу, что из себя представляет Codeception, поскольку многие с ним работали.
</p>
<p><img src="https://yupe.ru/uploads/image/f15d965fe5c4629f8df49f26f2a01fa5.png">
</p>
<p>Codeception работает по модели акторов. После того, как вы его затягиваете в проект и инициализируете, генерируется такая структура.
</p>
<p> У нас есть yml-файлы, вот там снизу — <em>functional.suite.yml</em>, <em>integration.suit.yml</em>, <em>unit.suite.yml</em>. В них создается конфигурация ваших тестов. Есть папочки под каждый вид тестов, где эти тесты лежат, есть 3 вспомогательных папочки:
</p>
<ul>
	<li><em>_data</em> — для тестовых данных;</li>
	<li><em>_output</em> — куда кладутся отчеты (xml, html);</li>
	<li><em>_support</em> — куда кладутся какие-то вспомогательные хелперы, функции, все, что вы напишите, чтобы использовать в ваших тестах.</li>
</ul>
<p>Для начала я расскажу, что мы взяли от Codeception и используем из коробки, ничего не дорабатывая, не решая дополнительных задач или проблем.
</p>
<p>Стандартные модули
</p>
<ol>
	<li><strong>PhpBrowser</strong></li>
	<li><strong>REST</strong></li>
	<li><strong>Db</strong></li>
	<li><strong>Cli</strong></li>
	<li><strong>AMQP</strong></li>
</ol>
<p>Первый такой модуль — это PhpBrowser. Этот модуль —  обертка над Guzzle, который позволяет взаимодействовать с вашим приложением: открывать странички, заполнять формы, сабмитить формы. И если вам не важно кроссбраузерное и в принципе браузерное тестирование, если вы вдруг тестируете UI, можно использовать PhpBrowser. Как правило, в наших UI-тестах мы его и используем, потому что нам не нужно какой-то сложной логики взаимодействия, нам достаточно открыть страничку и что-то небольшое там сделать.
</p>
<p>Второй модуль, который мы используем, — REST. Думаю, из названия понятно, что он делает. Для любых http-взаимодействий можно использовать этот модуль. В нем, мне кажется, решены практически все взаимодействия — хедеры, cookie, авторизация. Все, что нужно, в нем есть.
</p>
<p>Третий модуль, который мы используем из коробки, — это модуль Db. В последних версиях Codeception туда добавлена поддержка не одной, а нескольких баз данных, поэтому, если вдруг в вас в проекте несколько баз данных, теперь это работает из коробки.
</p>
<p>Есть такой модуль Cli, который позволяет запускать <em>shell-</em> и <em>bash-команды</em> из тестов, и мы его тоже используем.
</p>
<p>Есть модуль AMQP, который работает с любыми брокерами сообщений, которые основаны на этом протоколе. Хочу заметить, что официально он протестирован на RabbitMQ. Поскольку мы используем RabbitMQ, у нас с ним все окей.
</p>
<p>Это то, что мы используем из коробки. На самом деле, Codeception, по крайней мере, в нашем случае покрывает 80-85% всех нужных нам задач. Но над кое-чем все-таки пришлось поработать.
</p>
<p>Начнем с SOAP.
</p>
<p><span></span>
</p>
<p><img src="https://yupe.ru/uploads/image/1f3df3a2baec8b24ceee93b7671f890d.png">
</p>
<p>В наших сервисах кое-где есть SOAP-эндпоинты, их нужно тестировать, дергать, что-то с ними делать. Но вы скажете, что в Codeception есть такой модуль, который позволяет отправлять запросы и что-то потом делать с ответами. Как-то парсить, проверочки добавлять и все окей. Но SOAP-модуль не работает из коробки с несколькими эндпоинтами SOAP.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/2c4c6b3e8ecec9297b5195c0edb0adde.png">
</p>
<p>То есть, если у вас в приложении несколько SOAP-эндпоинтов, а у нас есть монолиты, у которых несколько WSDL, несколько SOAP-эндпоинтов, то нельзя в Codeception-модуле это так сконфигурировать в yml-файле, чтобы работать сразу с несколькими.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/c489da9a26fe449497f5e1e1c66a3563.png">
</p>
<p>У Codeception есть динамическая реконфигурация модуля, и вы можете написать какой-то свой адаптер, чтобы получать модуль, например, модуль SOAP, и динамически его реконфигурировать. В данном случае — подменять эндпоинт и используемую схему. Тогда в тесте, если вам нужно поменять эндпоинт, на который вы хотите отправить запрос, получаем наш адаптер и меняем на новый эндпоинт, на новую схему и отправляем на нее запрос.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/279a29e8bdcdb5f1b26effe40306602f.png">
</p>
<p>Второй момент, чего нет в Codeception. В Codeception нет работы с Kafka и нет никаких сторонних более-менее официальных аддонов, чтобы работать с Kafka.
</p>
<p>В этом нет ничего страшного, мы написали свой модуль.
</p>
<p><span></span>
</p>
<p><img src="https://yupe.ru/uploads/image/666a9aa28d136cd42259278d03eac0f7.png">
</p>
<p>Так он конфигурируется в yml-файле. Задаются некоторые настройки, для брокеров, для консьюмеров и для топиков. Эти настройки, когда вы пишете свой модуль, можно потом подтянуть в модули функцией <em>initialize </em>и этот модуль инициализировать этими настройками. И, собственно, у модуля реализовать все остальные методы — положить сообщение в топик, считать его, — все, что вам необходимо от этого модуля.<br>
</p>
<p><img src="https://yupe.ru/uploads/image/d7211c58a4097d3fc4bad4232a3d54ab.png">
</p>
<p>Модули для Codeception писать легко. Ок.
</p>
<p>Идем дальше. Как я уже сказал, в Codeception есть модуль Cli — обертка для <em>shell-команд</em> и работы с их output’ом.
</p>
<p><img src="https://yupe.ru/uploads/image/ae45142f605c533c0c6ded6bde66c7c1.png">
</p>
<p>Но иногда <em>shell-команду</em> нужно запустить не в тестах, а в приложении. Вообще тесты и приложения — это немного разные сущности, они могут лежать в разных местах. Тесты могут запускаться в одном месте, а приложение может быть в другом.
</p>
<p>Есть у кого-то потребность запускать <em>shell-команды</em> в тестах? Есть, ура!
</p>
<p>Я расскажу, зачем нам нужно запускать <em>shell</em> в тестах. У нас есть в приложениях команды, которые, например, разбирают очереди в RabbitMQ, двигают объекты по статусам. Эти команды в прод-режиме запускаются из-под супервизора, супервизор следит за их выполнением — если они упали, заново их запускает и так далее.
</p>
<p>Когда мы тестируем, супервизор у нас не запущен. Иначе тесты становятся нестабильными, непредсказуемыми, и мы сами хотим управлять запуском этих команд внутри приложения. Поэтому нам нужно из тестов запустить эти команды в приложении. У нас используются два варианта. Что один, что другой — в принципе, все то же самое, и все работает.
</p>
<p>Как запускать shell в приложении?<strong></strong>
</p>
<ol>
	<li><strong>Запускать тесты в контейнере, где находится приложение</strong></li>
	<li><strong>Запускать тесты в отдельном контейнере, но сделать его таким же как приложение</strong></li>
</ol>
<p>Первое: запускать тесты в том же самом месте, где находится приложение. Поскольку все приложения у нас в Docker’е, тесты можно запускать в том же контейнере, где находится сам сервис.
</p>
<p>Второй вариант: делать под тесты отдельный контейнер, некоторый <em>test</em> <em>runner</em>, но делать его таким же, как приложение. То есть из того же Docker-образа, и тогда все будет работать аналогично.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/c7216a0edc64733932accc89c7500a49.png">
</p>
<p>Еще одна задача, с которой мы столкнулись в тестах, — это работа с различными файловыми системами. Это пример, с чем можно и нужно работать, для нас актуальны первые три.
</p>
<p>С чем нужно работать
</p>
<ol>
	<li>Webdav</li>
	<li>FTP/SFTP</li>
	<li>AWS S3</li>
	<li>Local</li>
	<li>Azure, Dropbox, google drive</li>
</ol>
<p>Это Webdav, SFTP и амазоновская файловая система. Если порыться в Codeception, можно найти какие-то модули практически для любой более-менее популярной файловой системы.<br>
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/ac0ce0055f2a2517c75e3916ee4442af.png">
</p>
<p>Единственное, что я не нашел, это для Webdav’a. Но это все таки файловый системы, они плюс-минус одинаковые в плане внешней работы с ними, и нам хочется работать с ними одинаково.
</p>
<p>Мы написали свой модуль, он называется Flysystem, он лежит на <a href="https://github.com/lamoda/codeception-flysystem" target="_blank">Github</a> в открытом доступе и поддерживает 2 файловые системы — SFTP и Webdav —  и позволяет работать с обеими по одинаковому API. <br>
</p>
<p><img src="https://yupe.ru/uploads/image/e090f2e9a497cb64e829d900c34bd712.png">
</p>
<p>Получить список файлов, почистить директорию, записать файл, и так далее. Если туда добавить еще и амазоновскую файловую систему, наши, по крайней мере, потребности, точно покроются.
</p>
<p>Если кому-то вдруг надо — заходите, смотрите.
</p>
<p>Следующий момент, я считаю, очень важный для автотестов, тем более системного уровня, — это работа с базами данных. Вообще, хочется чтобы было, как на картинке,  — ВЖУХ и все завелось, заработало, и вот эти базы данных в тестах меньше бы поддерживать.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/39c73f0047cf6a8b92723e481df2b3dd.png">
</p>
<p>Какие я вижу здесь основные задачи.
</p>
<ol>
	<li><strong>Как раскатать БД нужной структуры - Db</strong></li>
	<li><strong>Как заполнить БД тестовыми данными - Db, Fixtures</strong></li>
	<li><strong>Как делать выборки и проверки - Db</strong></li>
</ol>
<p>Для всех 3-х задач в Codeception есть 2 модуля — Db, про который я уже говорил, другой называется Fixtures.
</p>
<p>Из этих 2 модулей и 3 задач мы используем только DB для третьей задачи.
</p>
<p>Для первой задачи можно использовать DB, там можно сконфигурировать SQL-дамп, из которого будет разворачиваться база данных, ну и модуль с фикстурами, думаю, понятно, зачем нужен.
</p>
<p> Там будут фикстуры в виде массивов, которые можно персистить в базу данных.
</p>
<p>Как я сказал, первые 2 задачи мы решаем немного по-другому, сейчас я расскажу, как мы это делаем.
</p>
<p>Разворачивание БД<strong></strong>
</p>
<ol>
	<li><strong>Поднимаем контейнер с PostgreSQL или MySQL</strong></li>
	<li><strong>Накатываем все миграции с помощью doctrine migrations</strong></li>
</ol>
<p>Первое — про разворачивание базы данных. Каким образом у нас происходит это в тестах. Мы поднимаем контейнер с нужной базой данных — либо PostgreSQL, либо MySQL, потом накатываем все нужные миграции с помощью <em>doctrine migrations</em>. Все, база данных нужной структуры готова, ее можно использовать в тестах.
</p>
<p>Почему мы не используем дапм — потому что тогда его не нужно поддерживать. Это какой-то дамп, который лежит с тестами, который нужно постоянно актуализировать, если что-то меняется в базе данных. Есть миграции — не нужно поддерживать дамп.
</p>
<p>Второй момент — создание тестовых данных. Мы не используем модуль Fixtures от Codeception, мы используем <em>Symfony-бандл</em> для фикстур.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/dacd9def490eaaf812255f0ee9865481.png">
</p>
<p>Здесь есть ссылка на него и пример того, как можно создавать фикстуру в базу данных.
</p>
<p> У вас фикстура тогда будет создаваться как некоторый объект предметной области, ее можно заперсистить в базу данных, и тестовые данные будут готовы.
</p>
<p>Почему DoctrineFixtureBundle?
</p>
<ol>
	<li><strong>Проще создавать цепочки связанных объектов</strong></li>
	<li><strong>Меньше дупликации данных, если фикстуры для разных тестов похожи</strong></li>
	<li><strong>Меньше правок при изменении структуры БД</strong></li>
	<li><strong>Фикстуры-классы гораздо нагляднее чем массивы</strong></li>
</ol>
<p><br>
</p>
<p>Почему мы его используем? Да по той же причине — эти фикстуры гораздо проще поддерживать, чем фикстуры от Codeception. Проще создавать цепочки связанных объектов, потому что это все заложено в Symfony-бандл. Нужно меньше дублировать данные, потому что фикстуры можно наследовать, это классы. Если меняется структура базы данных, эти массивы всегда нужно править, а классы — не всегда. Фикстуры в виде объектов предметной области всегда нагляднее, чем массивы.
</p>
<p>Про базы данных поговорили, поговорим немного про моки.
</p>
<p>Поскольку это тесты достаточно высокого уровня, которые тестируют систему целиком и поскольку наши системы достаточно сильно взаимосвязаны, понятно, что есть некоторые обмены и взаимодействия. Сейчас мы поговорим про моки на взаимодействие между системами.
</p>
<p><br>
</p>
<p>Правила для моков<strong></strong>
</p>
<ol>
	<li><strong>Мокаем все внешние http-взаимодейтвия сервиса</strong></li>
	<li><strong>Проверяем не только позитивные, но и негативные сценарии</strong></li>
</ol>
<p>Взаимодействия — это некоторые http-взаимодействия по REST или SOAP. Все эти взаимодействия в рамках тестов мы мокируем. То есть у нас в тестах нигде не идет реального обращения к внешним системам. Это делает тесты стабильными. Потому что внешний сервис может работать, может не работать, может медленно отвечать, может быстро, в общем, неизвестно, какое у него поведение. Поэтому мы все это покрываем моками.
</p>
<p>Еще у нас есть такое правило. Мы мокаем не только позитивные взаимодействия, но и стараемся проверять какие-то негативные кейсы. Например, когда сторонний сервис отвечает 500ой ошибкой либо выдает какую-то более осмысленную ошибку, — это все стараемся проверять.
</p>
<p>Для моков мы используем Wiremock, сам Codeception поддерживает…, у него есть такой официальный аддон Httpmock, но Wiremock нам понравился больше. Каким образом он работает?
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/0ab96547b34a948d0dd360b604acf139.png">
</p>
<p>Wiremock поднимается как отдельный Docker-контейнер во время тестов, и все запросы, который должны идти ко внешней системе, идут на Wiremock. <br>
</p>
<p><img src="https://yupe.ru/uploads/image/02cfeec1ac2cfbe4b04b895515ca1a6c.png">
</p>
<p>У Wiremock, если посмотреть на слайд — там есть такой квадратик, Request Mapping, у него есть набор таких маппингов, которые говорят о том, что, если пришел такой запрос, надо отдать такой ответ. Все очень просто: пришел запрос — получил мок.
</p>
<p>Моки можно создавать статически, тогда контейнер, когда уже с Wiremock поднимется, эти моки будут доступны, их можно использовать в ручном тестировании.  Можно создавать динамически, прямо в коде, в каком-нибудь тесте.
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/e479a2a9124e42e81bc3f6ca633b524e.png">
</p>
<p>Здесь приведен пример, как создать мок динамически, вы видите, описание достаточно декларативное, из кода сразу понятно, что за мок мы создаем: мок для метода GET, который придет на такой URL, и, собственно, что вернуть.
</p>
<p><img src="https://yupe.ru/uploads/image/ea5152c3fd5451cbfa7cc6e890470f3c.png">
</p>
<p>Кроме того, что этот мок можно создать, у Wiremock есть возможность потом еще и проверить, какой запрос ушел на этот мок. Это тоже бывает очень полезно в тестах.
</p>
<p>Про сам Codeception, наверное, все, и несколько слов о том, как запускаются наши тесты, и немного инфраструктурщины.
</p>
<p>Что у нас используется?
</p>
<p><img src="https://yupe.ru/uploads/image/09a758111b5c8e40cf925a255c30f195.png">
</p>
<p>Ну, во-первых, все сервисы у нас в Docker, поэтому запуск тестового окружения представляет собой поднятие нужных контейнеров.
</p>
<p>Для внутренних команд используется Make, в качестве CI используется Bamboo.
</p>
<p>Как выглядит запуск тестов на CI?
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/164b6577d02206fd45e84f6315ff0538.png">
</p>
<p>Сначала мы билдим нужную версию приложения, потом поднимаем окружение — это приложение, все сервисы, которые ему нужны, вроде Kafka, Rabbit, база данных и на базу данных мы накатываем миграцию.
</p>
<p>Все это окружение поднимается с помощью Docker Compose. Именно в CI, на проде все контейнеры крутятся под Kubernetes. Затем запускаем тесты и прогоняем.
</p>
<p>Сколько времени это все занимает?
</p>
<p>Все зависит от конкретного сервиса, но, как правило, подъем окружения до запуска тестов — это 5-10 минут, тесты — от 6 до 30 минут.
</p>
<p><img src="https://yupe.ru/uploads/image/ead7ba3a1defe133595c217ec4969fff.png">
</p>
<p><br>
</p>
<p>Сразу предупрежу этот вопрос, пока все тесты гоняются в одном потоке.
</p>
<p>Ну и такой вопрос. Как часто надо запускать тесты? Конечно, чем чаще, тем лучше. Чем раньше вы сможете поймать проблему, тем быстрее вы сможете ее решить.
</p>
<p>У нас есть 2 главных правила. Когда задача переходит в тестирование, на ней должны проходить все тесты, и unit, и не unit-тесты. Если какие-то тесты не проходят, это повод перевести задачу в фиксинг.
</p>
<p>Естественно, когда мы выкатываем релиз. На релизе все тесты должны обязательно проходить.
</p>
<p>В конце мне хотелось бы сказать что-то воодушевляющее — пишите тесты, пусть они будут зелеными, используйте Codeception, делайте моки. Думаю, вы все это прекрасно понимаете. На этом все, я готов ответить на ваши вопросы.
</p>
<p>***
</p>
<p>ВОПРОСЫ
</p>
<p>В: — Вопрос по поводу интеграций. Понятно, что если интеграции есть и они как-то регулируются, мы должны удостовериться, что наш код работает. Если у нас интеграция с внешним сервисом и он не соблюдает какие-то договоренности, но это критический функционал (например, пару лет назад у нас было такое с Яндекс.Деньгами — они меняли формат ответов или принимающегося запроса), что делать в этом случае? Есть ли у вас такие интеграции, как вы их мониторите, тестируете или что-то вроде того?
</p>
<p>О: — У нас есть интеграции с внешними системами, прежде всего, это связано с платежными системами, но наши тесты, которые мы прогоняем, на которые завязаны релизы, в том числе и внешние сервисы, мы стараемся все покрывать моками. Если что-то меняется в интеграции не по вашей вине, не факт, что вы поймаете это в автотестах, скорее всего, вы поймаете это в продакшене. Скорее всего, это просто будет влиять на стабильность тестов, тем более, не всегда тестовую среду можно интегрировать с живой внешней системой. Иногда это доставляет гораздо больше проблем, чем использование моков.
</p>
<p>*
</p>
<p>В: — Хотелось бы спросить по поводу тестирования UI в связке с бэкендом. Когда мы использовали Codeception для этого, у нас возникли проблемы с использованием PhpBrowser. Ну не проблемы, ожидаемое поведение. Он не умеет исполнять JS, и проверки того, что на странице присутствует какой-то элемент, заканчиваются просто поиском этого элемента в коде HTML-странице. У него может быть просто атрибут “<em>hidden</em>” включен, и тест ничего скажет. Вы как-то сталкивались с такими проблемами? Не используете ли вы тот же самый WebDriver или все на PhpBrowser?
</p>
<p>О: — В тех тестах, где нужен Ajax и JS, какие-то проверки, мы используем WebDriver. Там где в UI не надо проверять, а достаточно сделать какое-то действие, без JS, достаточно использовать PhpBrowser.
</p>
<p>*
</p>
<p>В: — Как составляются сценарии для flow-тестов — на основе статистики, или QA-инженеры, или и то, и то?
</p>
<p>О: — Про flow-тесты. В каждом сервисе есть определенные критичные объекты предметной области. В случае управления заказами — это, собственно, заказ. В случае, там, например, системы, которая делает возвраты денег клиентам, — это сам возврат. Для таких критичных объектов пишутся flow-тесты. Они пишутся только на happy path’ы, мы не проверяем здесь негативные сценарии. Нам важно, чтобы для критичных объектов проходил полностью положительный сценарий и мы могли быть уверены в том, что это будет работать. Сценарии для flow-тестов у нас могут писать как тестировщики, так и разработчики — ревью тестов у нас совместное. Если пишутся автотесты, их ревьюят и разработчики, и тестировщики. Если мнения о том, где, что и как нужно проверить, расходятся, это всегда будет обозначено.
</p>]]></summary>
<link href="https://yupe.ru:443/post/testy-na-codeception-dlya-php-bekendov-pavel-stashevskiy-qa-engineer-lamoda.html"></link>
<id>urn:uuid:4c0a6c1f-a28f-270d-3859-b97e141da8cf</id>
</entry>
<entry>
<title>Две классные штуки MySQL 8</title>
<author>xoma</author>
<published>2019-01-24T00:00:00+03:00</published>
<updated>2019-01-24T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p>- Да у вас даже check-ов нет!</p><p>Обычно такая фраза звучит в очередном споре о том, что же лучше MySQL или PostgreSQL. И стоит сказать это справедливое замечание.<br>Действительно, в MySQL до версии 8 не было check сonstraints. Чисто формально синтаксис check при создании колонок в таблице поддерживался, но по факту никаких проверок не создавалось.<br>Это было сделано для того, чтобы облегчить миграцию с других СУБД, где check полноценно работал. И вот в 2019 спустя 15 лет (!) check появится в MySQL 8.0.15. Историю этого вопроса можно отследить по тикету в официальном баг-трекре MySQL - <a href="https://bugs.mysql.com/bug.php?id=3464">https://bugs.mysql.com/bug.php?id=3464</a><span class="redactor-invisible-space"> Основной причиной почему так долго пилили называют полную переделку "Data dictionary", котороую сделали только в 8.0 и без которой реализация check невозможна. </span></p><p><span class="redactor-invisible-space">Еще один пункт, за который любят пинать MySQL - отсутствие параллельных запросов. Но и тут начались подвижки в версии 8.0. Пока параллельно выполняться могут только запросы вида "SELECT count(*)....", но это только начало. Подробнее в блоге Percona - <a href="https://www.percona.com/blog/2019/01/23/mysql-8-0-14-a-road-to-parallel-query-execution-is-wide-open/">https://www.percona.com/blog/2019/01/23/mysql-8-0-...</a><span class="redactor-invisible-space"></span></span></p><p><span class="redactor-invisible-space"><br></span></p><p><span class="redactor-invisible-space"><br></span></p><p><br></p>]]></summary>
<link href="https://yupe.ru:443/post/dve-klassnye-shtuki-mysql-8.html"></link>
<id>urn:uuid:79222ddc-bf4f-9c2f-27fe-28a68dc6f656</id>
</entry>
<entry>
<title>Поддержка и доработка сайта Суши-Тайм</title>
<author>xoma</author>
<published>2018-12-06T00:00:00+03:00</published>
<updated>2018-12-06T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p><img src="https://yupe.ru/uploads/image/df781fca3a23636f8e5a75e8ffc9fc89.png">
</p>
<p><img src="https://yupe.ru/uploads/image/b829f26fa671aab65a244c3b487dd235.png">
</p>
<p><img src="https://yupe.ru/uploads/image/ba2d1d56bc46fe96a3423c9879b3c0c0.png">
</p>
<p><img src="https://yupe.ru/uploads/image/8d70cbd4e3f76f0fe031a5a1cddd045f.png">
</p>]]></summary>
<link href="https://yupe.ru:443/post/dostavka-blyud-yaponskoy-kuhni.html"></link>
<id>urn:uuid:411a1b92-a0a0-3e95-4d92-9c4ebc22dd0c</id>
</entry>
<entry>
<title>Интернет-магазин нужных товаров</title>
<author>xoma</author>
<published>2018-12-06T00:00:00+03:00</published>
<updated>2018-12-06T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p><img src="https://yupe.ru/uploads/image/de6535a4eb4aac1a6db5db12f24fe321.png">
</p>
<p><img src="https://yupe.ru/uploads/image/743d4b08f43face0fbf828e3ccc8ff1e.png">
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/626bb236ad711453e0f24a857b189360.png">
</p>
<p><img src="https://yupe.ru/uploads/image/27620053960364e1278ab079359851ef.png">
</p>
<p><img src="https://yupe.ru/uploads/image/9ca89fcdd00da052afea9af3fd0c173c.png">
</p>]]></summary>
<link href="https://yupe.ru:443/post/internet-magazin-nuzhnyh-tovarov.html"></link>
<id>urn:uuid:a054da40-3b2d-999d-92f3-9d65fe4336f9</id>
</entry>
<entry>
<title>Доработка и интеграция сайта бренда PANFIL</title>
<author>xoma</author>
<published>2018-12-04T00:00:00+03:00</published>
<updated>2018-12-04T00:00:00+03:00</updated>
<summary type="html"><![CDATA["
<p><img src="https://yupe.ru/uploads/image/d18422b89b585195ddaa3f90edbb3347.png" "="">
</p>
<p><img src="https://yupe.ru/uploads/image/807e8959e0af017977e706a86a5ec8eb.png">
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/7ae52d6c2cc5dc0c323027f51dab3a4c.png" "="">
</p>
<p><br>
</p>
<p><img src="https://yupe.ru/uploads/image/5fb1167b9902893bbe2815c0f30e697a.png">
</p>]]></summary>
<link href="https://yupe.ru:443/post/oficialnyy-sayt-modnogo-brenda-panfil.html"></link>
<id>urn:uuid:db776adf-c96a-8039-975a-c9c0a46fa0c8</id>
</entry>
</feed>