Pull to refresh
0
0
Anton Maznoy @Kunokput

Пользователь

Send message
В целом со статьей согласен, но
И так, быстрый гуглёж на тему тестов находит просто уйму статей, которые в своей основной массе делятся на две категории

Быстрый гуглеж по запросам «php/symfony/etc mock unit tests» приводит на конкретные статьи из документации по PHPUnit, Symfony и т.п. в которых все расписано конкретно и по делу, без перегрузки лишними подробностями, вроде CI.
Я наткнулся на пример зарплатного калькулятора из документации Symfony. Калькулятор — калькулятору рознь.
Чем ближе покрытие к 100% — тем надёжнее тестирование

Чем ближе покрытие к 100%, тем больше программисты гонятся за красивыми цифрами покрытия и тем меньше пишут адекватных тестов.
Для тестирования старых проектов чаще всего придётся сначала здорово отрефакторить их.

В таком случае придется писать интеграционные тесты. Зачем подвергать бизнес рискам, рефакторя без тестов?
А как вы себе представляете внедрение этих методик на уровне код-ревью?
Большая часть принципов, описанных здесь, являются лишь симптомами проблем, связанных с неправильным выбором архитектурных решений.
Методы больше 20 строк, switch операторы, наличие или отсутствие контекста — все это следствие неправильно выбранных границ модулей или неправильный абстракций.
Так может на код-ревью лучше следует смотреть на правильность реализации бизнес логики и правильность выбора архитектурных решений, а не на эти принципы?
Если подходить к этим принципам, как к симптомам возможных проблем, то их можно использовать, но использовать их в код ревью как аргументы, я бы не стал.

Конечно можно на уровне команды вынести конвенции по именам классов, методов, переменных и т.п., польза от этого правда очень незначительна. Хотя, если это конечно поможет не называть переменные
a1, a2 и т.п., то уже хорошо.

Так же некоторые принципы можно отнести к странным решениям:
  1. Вместо assertEquals использовать assertExpectedEqualsActual
  2. Изолировать блоки try/catch
  3. Использовать префиксы get/set

Поэтому форвардящий декоратор можно вынести в отдельный класс и наследоваться от него.

// Wrapper class - uses composition in place of inheritance
public class InstrumentedSet<E> extends ForwardingSet<E> {
 private int addCount = 0;
 public InstrumentedSet(Set<E> s) {
 super(s);
 }
 @Override public boolean add(E e) {
 addCount++;
 return super.add(e);
 }
 @Override public boolean addAll(Collection<? extends E> c) {
 addCount += c.size();
 return super.addAll(c);
 }
 public int getAddCount() {
 return addCount;
 }
}
// Reusable forwarding class
public class ForwardingSet<E> implements Set<E> {
 private final Set<E> s;
 public ForwardingSet(Set<E> s) { this.s = s; }
 public void clear() { s.clear(); }
 public boolean contains(Object o) { return s.contains(o); }
 public boolean isEmpty() { return s.isEmpty(); }
 public int size() { return s.size(); }
 public Iterator<E> iterator() { return s.iterator(); }
 public boolean add(E e) { return s.add(e); }
 public boolean remove(Object o) { return s.remove(o); }
 public boolean containsAll(Collection<?> c)
 { return s.containsAll(c); }
 public boolean addAll(Collection<? extends E> c)
 { return s.addAll(c); }
 public boolean removeAll(Collection<?> c)
 { return s.removeAll(c); }
 public boolean retainAll(Collection<?> c)
 { return s.retainAll(c); }
 public Object[] toArray() { return s.toArray(); }
 public <T> T[] toArray(T[] a) { return s.toArray(a); }
 @Override public boolean equals(Object o)
 { return s.equals(o); }
 @Override public int hashCode() { return s.hashCode(); }
 @Override public String toString() { return s.toString(); }
}


Статья очень большая, ее трудно осилить.
Автор не упомянул простое правило, которым можно руководствоваться, при создании иерархии типов: хочешь наследования? Напиши 3 тестовые реализации сам и дай 1 реализацию написать соседу. Лень? извольте в композицию

Information

Rating
Does not participate
Registered
Activity