Вообщем по просьбе Viktor Davion объясняю (в 140 твиттеровских символов не уложился), отчасти, что побудило к посту ненависти. Под катом, т.к. содержит плоды работы воспалённого мозга.
понедельник, 6 декабря 2010 г.
пятница, 5 ноября 2010 г.
Code coverage для интеграционного тестирования
Итак, ситуация следующая. Есть у нас Integration Test Framework, написанный специально для нашего приложения. Но начался он писаться задолго после старта работы над приложением, поэтому накопилось большое количество кода, который тестируется в лучшем случае unit тестами. Как это всё вместе работает не совсем понятно :) Для это писался этот ITF, но он ещё в разработке и тесты пишутся достаточно медленно. Вот в процессе обсуждение этого факта, мне подумалось, а почему бы не считать каково покрытие тестами у нас. Вообще, было замечено, что многие программисты очень плохо относятся ко всяким таким характеристикам как сode coverage. Вот для unit тестов оно у нас не считается, но локально иногда запускаешь посмотреть, сейчас у нас больше 80%. Но, как мне сказали, о чём это говорит? Из за неправильного ответа на этот вопрос и возникает такая не любовь к этой величине. То, что сode coverage 100% это не значит, что вы достигли просветление, ваше приложение идеально и вы на пути к нирване. Но это говорит о том, что весь написанный код, действительно вызывается, т.е. это немного спасает от простых ошибок.
Но вернёмся к ITF. Почему я считаю, что для интеграционных тестов сode coverage более полезен.
- Меньше возможностей для искусственной манипуляции результатами. Как иногда бывает с простыми численными характеристиками, за что их и не любят, люди увлекаются попыткой их достижения. И если мы говорим о unit тестах, то достаточно просто написать много, по своей сути бесполезных тестов, которые дадут требуемую цифру. Это ещё одна из причин, почему test first :) Но для интеграционных тестов это не так. Они не работают напрямую с методами или классами. Они используют исключительно пользовательское api, создание файла, нажатие на кнопку и т.п. Поэтому пытаться искусственно делать 100% покрытие очень трудоёмкая задача. Кроме того, даже для ручного тестирования, многие все равно прикручивают сode coverage, чтобы увидеть это заветное число, после того как тестировщики погоняют приложение. Это даёт много интересной информации для размышления.
- Мы оказались в ситуации, что интеграционные тесты приходится писать вдогонку. Уже есть большая часть приложения, команда разрослась почти до 10 человек и всего 2 тестировщика. Таким образом, отслеживая величину покрытия, можно делать выводы о том, мы догоняем или всё таки уже опоздали. Т.е. если на предыдущей недели было 20%, а на этой стало 23%, то мы на верном пути, но если стало 10%, то слишком увлеклись генерированием не проверенного кода и наверно стоит поднапрячь программистов, чтобы они побольше внимания уделяли написанию интеграционных тестов в помощь тестировщикам.
суббота, 30 октября 2010 г.
Субботний вечер пропаганды маргинальных технологий: hgsubversion
Как можно было заметить, обычно в блоге пишу про рабочие моменты. Поэтому решил отдельно выделить записи, которые будут посвященных не очень популярных в "энтерпрайз" кругах, технологиях и темах :)
Сегодня хотел немного затронуть тему распределенных систем контроля версий. На самом деле, эта тема достаточно популярно, просто больших компаниях приживается очень плохо, некоторые, как я узнал, например в IBA, вообще cvs пользуются. Подробно рассказывать именно про DVCS не думаю, что есть смысл, материалов на всех языках мира хватает, как в общем про DVCS, так и про конкретные системы. Один из главных зачинатилей всего безобразия, это Линус, так что в качестве введения можно посмотреть его выступление в кампусе Google, где в его духе эмоционально рассказано кто не прав и почему :)
Я расскажу про немного другую проблему. Когда вы делаете не свой продукт, а под заказ или являетесь всего лишь небольшим подпроектом огромного проекта, часть технологий вы выбрать не можете и вынужденны использовать subversion. Но ведь хочется вкусненького, и это возможно. Почти все разработчики DVCS понимают, что все сейчас прям не бросятся переводить свои репозитории на новые рельсы. Поэтому присутствует интеграция с многими другими системами контроля версий. Например с subversion. Вот хотел бы рассказать про hgsubversion, проект, который позволяет интегрировать subversion и mercurial репозитории. Был опробован на реальном проекте и пока никаких проблем не возникло.
пятница, 29 октября 2010 г.
Требую больше immutability!
Вообщем тривиальная слоган, его можно прочитать даже в достаточно старой Joshua Bloch: Effective Java, Item 15: Minimize mutability.
Под катом чутка банальности, так как сегодня доделал и наболело.
пятница, 22 октября 2010 г.
Java сompile time annotations
- Требовать, чтобы все ссылочные типы также были immutable, как String например. Для коллекций можно использовать Collections.unmodifiableCollection метод.
- Второй способ заключается в основе ООП: инкапсуляции. Ни один метод не возвращает объект по ссылке, только его копию, например вместо возврата коллекции можно вернуть её неизменяемую копию с помощью описанного выше метода.
четверг, 16 сентября 2010 г.
Log4j file header
- <appender name="eventhistoryfile" class=" org.apache.log4j.RollingFileAppender">
- <param name="LogFileName" value="${log4j.file.name}"/>
- <layout class="org.apache.log4j.PatternLayout">
- <param name="ConversionPattern" value="${log4j.pattern}"/>
- </layout>
- </appender>
- @Override
- protected void writeHeader() {
- if (this.layout == null) {
- return;
- }
- String header = layout.getHeader();
- if (header == null && this.qw == null) {
- return;
- }
- File f = new File(this.getFile());
- if (!f.exists() || (f.exists() && f.length() == 0)) {
- this.qw.write(header);
- }
- }
вторник, 22 июня 2010 г.
Crash Racing
Код не покажу, стыдно :)
Upd: Видео уже лень делать, вот скриншоты :)
воскресенье, 16 мая 2010 г.
Прикольный workaround для деплоя репортов в Reporting Services
rs.exe -i MyReportScript.rss -s http://localhost/reportserver
MyReportScript.rss - это скрипт на VB, который и загружает репорт или репорты.
Вот так примерно выглядит код загрузки:
- Try
- definition = New [Byte](stream.Length) {}
- stream.Read(definition, 0, CInt(stream.Length))
- stream.Close()
- Catch e As IOException
- Console.WriteLine (e.Message)
- End Try
- Try
- warnings = rs.CreateReport(reportName, reportsPath, True, definition, props)
- Console.WriteLine("Upload Report: {0}.", reportName)
- Catch e As Exception
- Console.WriteLine (e.Message)
- End Try
Репорты делал не я и делались они 2005 студии. У меня стоит 2008, после пары изменений, я получаю вот такое сообщение:
System.Web.Services.Protocols.SoapException: The report definition is not valid. Details: '.', hexadecimal value 0x00, is an invalid character. Line 4334, position 10.
Через Management Studio загружается нормально.
Конечно же файл репорта представляет из себя просто ужасный xml. И что мы видем в строчке 4334? Заключительный закрывающийся тэг:
Становится совсем не понятно, но всемогущий гугль легко находит линку:
RS.EXE fails to deploy a report upgraded from SQL Server 2005 Reporting Services - "report definition is not valid"
Предложенное решение проблемы меня просто поставило в тупик
- Replacing
- My_Byte_Array = New [Byte](stream.Length) {}
- With
- My_Byte_Array = New [Byte](stream.Length - 1) {}
Вообщем даже не представляю как это у них получилось cгенерировать такую проблему в новой студии, вообщем просто в шоке =D
Хотя я иногда и по круче отжиги делаю :)
З.Ы.: Сорри за не самую красивую подсветку, просто мой любимый Online syntax highlighter что-то с VB на отрез отказался работать, а HTML Source Code Syntax Highlighter хоть и подсвечивает код, но не очень красиво.
З.Ы.Ы.: Катя подсказала более крутой сервис для подсветки кода, так как ей нужен был Object C, то она нашла вот этот, количество языков поражает. Только надо выбрать опцию Combine Style and HTML, чтобы он не генерировал отдельно css стиль, а всё выдавал html кодом. Качество подсветки хорошее.
воскресенье, 18 апреля 2010 г.
Java - отличная платформа, но слишком неповоротливый язык.
Вот два небольших примера.
1. Есть очень популярная задача, организовать конвейер для обработки документов, которые по сути из себя представляют Map или, если говорим о C#, Dictionary. К Dictionary в дот нете, у меня есть одна большая претензия, он слишком часто кидает исключения. Но речь не об этом. Вот так выглядит простой документ, я добавил в него метод AddProperty, с помощью которого можно не только добавить элемент, но и перезаписать старый:
- public class Document : Dictionary<string, object>
- {
- public Document AddProperty(string name, object value)
- {
- if (ContainsKey(name))
- Remove(name);
- Add(name, value);
- return this;
- }
- }
API конвейера тоже простое:
- public class Pipeline
- {
- public void AddProccessor(Func<Document, Document> proccessor)
- {
- }
- public void Execute()
- {
- }
- }
Но самое интересное теперь. Иногда требуется добавить очень простую обработку, которая просто добавляет пару полей к документу. В java пришлось бы поступить как в свинге, анонимный класс с реализацией метода интерфейса листенера. Но в C# коллега подсказал очень и очень красивое решение:
- var pipeline = new Pipeline();
- pipeline.AddProccessor(record => record.AddProperty("NewProp", "Value").AddProperty("AnotherProp", 1));
2. На эту тему копий сломано не мало, перегрузка методов или параметры по умолчанию. Кто бы, что ни говорил, но C# 4.0 c параметрами по умолчанию плюс именование параметров, лично для меня, отличное, красивое и удобно решение.
Вот есть метод:
- public static string format(string value,
- bool option1 = false, bool option2 = false)
- {
- return value;
- }
- format("test", option2: true);
Ну и так, ещё одна рабочая мысль за сегодня: парное программирование - это не только весело, но и действительно продуктивно.
И совсем напоследок цитата дня (которую правда уже постил в Buzz'е, но уж больно понравилась):
- select fun, profit from real_world where relational=false
понедельник, 5 апреля 2010 г.
Java Decompiler
По старой традиции использовал для этих целей всегда JAD. Но он уже достаточно давно заброшен и не справляется с некоторыми фишками. Нашёл хорошую альтернативу, так прям и называется Java Decompiler. Работает как отдельное приложение, так и как плагин к эклипсу.
Больше всего не давали жить две штуки (исходники, которые мне нужны, были просто напичканы ими): ассерты и дженерики. Вот на составил небольшой демонстрационный примерчик:
Исходный текст:
- public class Example {
- public class Doing<T extends Some> extends Base<T> {
- @Override
- public T doSomething(T param) {
- assert(param == null);
- return param;
- }
- }
- public abstract class Base<T extends Some> {
- public abstract T doSomething(T param);
- }
- public class Some {
- public Some doing() {
- return this;
- }
- }
- }
- // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
- // Jad home page: http://www.kpdus.com/jad.html
- // Decompiler options: packimports(3)
- // Source File Name: Example.java
- public class Example {
- public abstract class Base {
- public abstract Some doSomething(Some some);
- final Example this$0;
- public Base() {
- this$0 = Example.this;
- super();
- }
- }
- public class Doing extends Base {
- public Some doSomething(Some param) {
- if (!$assertionsDisabled && param != null)
- throw new AssertionError();
- else
- return param;
- }
- final Example this$0;
- static final boolean $assertionsDisabled = !Example
- .desiredAssertionStatus();
- public Doing() {
- this$0 = Example.this;
- super();
- }
- }
- public class Some {
- public Some doing() {
- return this;
- }
- final Example this$0;
- public Some() {
- this$0 = Example.this;
- super();
- }
- }
- public Example() {
- }
- }
- public class Example {
- public abstract class Base<T extends Example.Some> {
- public Base() {
- }
- public abstract T doSomething(T paramT);
- }
- public class Doing<T extends Example.Some> extends Example.Base<T> {
- public Doing() {
- super(Example.this);
- }
- public T doSomething(T param) {
- assert (param == null);
- return param;
- }
- }
- public class Some {
- public Some() {
- }
- public Some doing() {
- return this;
- }
- }
- }
Подсветка кода
Ранее я писал как можно сделать подсветку исходного кода в записях с помощью SyntaxHighlighter. Но пару дней назад официальный сайт отвалился и код перестал подсвечиватся. По сути, если подумать, то делать это в динамике, как это делает SyntaxHighlighter нет смысла. Но на серверную сторону блогера повлиять трудно. Поэтому нашёл замечательный сайт. Идея просто, вставляет код, там используется SyntaxHighlighter от которого получаем статический html, и спокойно вставляем её в запись.Единственный недостаток, с <, > вообщем запрещёнными символами, которые, например, встречаются в LINQ выражениях. Придётся самостоятельно делать эксейпинг.
Да, и ещё одним преимуществом такого статического подхода, является корректное отображения кода в RSS ридерах и пр. трансляторах, js они не могу проинтерпретировать, а со статичным html'ем справляются на ура.
UPDATE: Не знаю как просмотрел, но есть отличная js библиотечка для динамической подсветки highlight.js от Ивана Сагалаева. Поддерживает целых 35 языков и всё время пополняется, кроме того, у неё есть отличный набор из более чем десятка тем. Я себе под новый дизайн выбрал Dark.
<?xml version="1.0"?>
<response value="ok" xml:lang="en">
<text>Ok</text>
<comment html_allowed="true"/>
<ns1:description><![CDATA[
CDATA is <not> magical.
]]></ns1:description>
<a></a> <a/>
</response>
понедельник, 22 марта 2010 г.
Ant Magic
Код простой. Класс 1 вызывает метод класса 2.
- import test.sub2.Sub2;
- public class Sub1 {
- public static void sub1() {
- Sub2.sub2();
- }
- }
- package test.sub2;
- public class Sub2 {
- public static void sub2() {
- }
- }
- <project name="TestProject" default="compile">
- <target name="compile">
- <delete>
- <fileset dir="bin">
- <include name="**/*.class"/>
- </fileset>
- </delete>
- <javac srcdir="src" destdir="bin">
- <exclude name="test/sub2/**" />
- </javac>
- </target>
- </project>
Но не тут то было, его тоже скомпилирует и всё пройдёт саксес. Хорошо тут специально симулированный простой случай, но начинается полная (_!_) когда Sub2 вызывает ещё какой-то класс, в котором есть специфические методы из библиотек, которых нет в classpath модуля1 и соответственно это умный ант разрулить не может. И человек рвёт на себе волосы, потому что по сути он всё сделал правильно, модуль 2 компилируется без проблем, какого падает компиляция модуля 1, хотя в его классах не было изменений, не понятно. Вообщем вот он какой magic.