390 likes | 651 Vues
TDD в Magento . С чего всё начиналось. Agenda. Введение в TDD Важные принципы при разработке ПО. Введение в TDD. Введение в TDD. Введение в TDD. Введение в TDD. Стили разработки через тестирования Traditional (традиционный) Active (активный) Acceptance (приёмочный).
E N D
Agenda • Введение в TDD • Важные принципы при разработке ПО
Введение в TDD Стили разработки через тестирования • Traditional (традиционный) • Active (активный) • Acceptance (приёмочный)
Введение в TDD Traditional(традиционный) • Разработка только через “сначала-тест-потом-код”. • Тесты диктуют архитектуру. • На первых итерациях получаются простые “тупые” решения задач.
Введение в TDD Active (активный) • Разработка только через “сначала-тест-потом-код”. • Разработчик продумывает дизайн решения задачи заранее и потом используя TDD пытается к нему прийти.
Введение в TDD Acceptance (приёмочный) • Разработчик пишет один большой тест на всю задачу вместо того, чтоб писать много маленьких. • На каждой итерации этот один тест постоянно увеличивается. • Разработчик использует множество Mock-объектов. • После того, как функциональность готова, заменяет Mock-объекты реальными классами.
Введение в TDD Только “Традиционный” стиль является настоящим TDD
Введение в TDD Минусы “Активного” стиля • Бессмысленно потраченное время на продумывание архитектуры, так как при TDD часто приходишь к другой архитектуре. • До решения некоторых задач возможна ситуация “design deadlock” (архитектурный тупик): “Традиционный” TDD помогает выйти из этой ситуации.
Введение в TDD Минусы “Приёмочного” стиля • Нарушение определения “unit”. Тест становится бесполезным для поиска и фикса багов. • Тест слишком большой и трудно понимаемый. • Тест намного меньше влияет на архитектуру решения, так как “тестируемость” кода не играет особой роли.
Что необходимо знать перед началом разработки через тестирование: • Основные принципы разработки ПО. • Паттерны проектирования. • Приёмы рефакторинга кода.
Основные принципы разработки ПО S.O.L.I.Dпринципы • SRP: Single Responsibility Principle (Принцип единственной ответственности) • OCP: Open Closed Principle (Принцип открытости-закрытости) • LSP: Liskov Substitution Principle (Принцип подстановки Лисков) • ISP: Interface Segregation Principle (Принцип сегрегации интерфейса) • DIP: Dependency Inversion Principle (Принцип инверсии зависимости)
Основные принципы разработки ПО SRP: Single Responsibility Principle (Принцип единственной ответственности) Цель: • У класса должна быть только одна причина для изменения. Иными словами класс должен уметь делать что-то одно.
Основные принципы разработки ПО SRP: Single Responsibility Principle (Принцип единственной ответственности) Пример Example 1:
Основные принципы разработки ПО SRP: Single Responsibility Principle (Принцип единственной ответственности) Решение:
Основные принципы разработки ПО OCP: Open Closed Principle(Принцип открытости-закрытости) Цель: • Программные элементы (классы, модули, функции) должны быть открыты для расширения, но закрыты для модификации.
Основные принципы разработки ПО Пример: class Logger { public function log($logText) { // save log into file } } class SmtpMailer { private $logger; public function __construct() { $this->logger = new Logger(); } public function sendMessage($message) { //... sending message $this->logger->log(sprintf("Message sent: '%s'", $message)); } }
Основные принципы разработки ПО Проблема: Что, если мы захотим поменять файловый логгер на логгер в базу данных? Мы изменимSmtpMailerкласс: public function __construct() { //$this->logger = new Logger(); $this->logger = new DatabaseLogger(); } Нарушение принципа!
Основные принципы разработки ПО Решение: interface ILogger { public function log($logText); } class Logger implementsILogger { public function log($logText) { // save log into file } } class SmtpMailer { private $logger; public function __construct(ILogger$logger) { $this->logger = $logger; } public function sendMessage($message) { //... sending message $this->logger->log(sprintf("Message sent: '%s'", $message)); } }
Основные принципы разработки ПО LSP: Liskov Substitution Principle (Принцип подстановки Лисков) Цель: • Функции, которые используют объекты определённых классов, не должны ломаться, если в них передать наследники от этих классов.
Основные принципы разработки ПО Для того, чтоб не нарушался этот принцип, класс должен иметь Контракт и наследники не должны егонарушать. Контракт – список публичных методов с ожидаемым поведением.
Основные принципы разработки ПО ISP: Interface Segregation Principle (Принцип сегрегации интерфейса) Цель: • Не следует заставлять клиентов зависеть от интерфейсов, которые им не нужны.
Основные принципы разработки ПО Пример: abstract class ServiceClient { public function getServiceUrl() {...} public function setServiceUrl($url) {...} abstract public function sendData($data); abstract public function flush(); } class HttpServiceClientextends ServiceClient { public function sendData($data) { $channel = Channel::openChannel($this->getServiceUrl()); $channel->send($data); } public function flush() { //do nothing! } } class BufferingHttpServiceClientextends ServiceClient { public function sendData($data) { $this->buffer .= $data; } public function flush() { $channel = Channel::openChannel($this->getServiceUrl()); $channel->send($this->buffer); } }
Основные принципы разработки ПО Решение: abstract class ServiceClient { public function getServiceUrl() {...} public function setServiceUrl($url) {...} abstract public function sendData($data); } abstract class BufferingServiceClientextends ServiceClient { abstract public function flush(); } class HttpServiceClientextends ServiceClient { public function sendData($data) { $channel = Channel::openChannel($this->getServiceUrl()); $channel->send($data); } } class BufferingHttpServiceClientextends BufferingServiceClient { public function sendData($data) { $this->buffer .= $data; } public function flush() { $channel = Channel::openChannel($this->getServiceUrl()); $channel->send($this->buffer); } }
Основные принципы разработки ПО DIP: Dependency Inversion Principle (Принцип инверсии зависимости) Цели: • Высокоуровневые модули или классы не должны зависить от низкоуровневых, оба должны зависеть от абстракций. • Абстракции не должны зависить от деталей реализации. Детали реализации должны зависеть от абстракций.
Основные принципы разработки ПО Стандартная архитектура приложения
Основные принципы разработки ПО Модифицированная архитектура
Основные принципы разработки ПО Пример:
Основные принципы разработки ПО Код: class KeyboardReader { public function read() {...} } class FileWriter { public function write($data, $filename) {...} } class Copier { protected $_reader; protected $_writer; public function __construct() { $this->_reader = new KeyboardReader(); $this->_writer = new FileWriter(); } public function copy($toFile) { $data = $this->_reader->read(); $this->_writer->write($data, $toFile); } }
Основные принципы разработки ПО Проблемы: • Класс Copier не тестируемый, так как нельзя изолироваться от KeyboardReaderи FileWriter. • Теперь в любом месте приложения ты должен использовать Copier только с классамиKeyboardReaderи FileWriter, даже если захочешь использоватьPrinterWriterкласс.
Основные принципы разработки ПО Решение:
Основные принципы разработки ПО Решение: interface IReader { public function read(); } interface IWriter { public function write($data); } class KeyboardReaderimplements IReader { public function read() {...} } class FileWriterimplements IWriter { public function __construct($filename) { $this->_filename = $filename; } public function write($data) {...} } class Copier { protected $_reader; protected $_writer; public function __construct(IReader$reader, IWriter$writer) { $this->_reader = $reader; $this->_writer = $writer; } public function copy() { $data = $this->_reader->read(); $this->_writer->write($data); } }
Основные принципы разработки ПО Что мы получили: • Лёгкое тестирование. Reader и Writer можем подменить Mock-объектами. • Гибкость. Теперь в любом месте приложения мы можем использовать Reader и Writer, какой захотим. • Намного более прозрачный контракт. В процессе разработки нам не нужно заглядывать в реализацию Reader и Writer, достаточно посмотреть интерфейсы, чтобы понять, что они делают.