Thursday, February 19, 2009

AddOne

Problem


Implement C# function int addone(int n) which will return n+1. You can NOT use any of the following arithmetical operations in the fuction body: + - * / ++ -- += -=.

Solution



using NUnit.Framework;

namespace Developer.Test.Net
{
[TestFixture]
public class TestModulus
{
[Test]
public void TestAddOne()
{
for (int i = -1000; i < 1000; i++)
{
Assert.AreEqual(i + 1, AddOne(i));
}

int j = int.MaxValue;

Assert.AreEqual(j + 1, AddOne(j));

j = int.MinValue;

Assert.AreEqual(j + 1, AddOne(j));
}

public static int AddOne(int n)
{
int position = 1;

while (position != 0)
{
if ((n & position) == position) {
n = n ^ position;
} else {
n = n | position;

break;
}

position = position << 1;
}

return n;
}
}
}

Wednesday, February 11, 2009

Использование Unit-тестов отдельно от TDD

В продолжение к этому комментарию.

> Если вам приходится тестировать приватные классы — значит у вас серьозные проблемы с архитектурой и пониманием TDD вообще.

Одно другому не мешает. Если я не использую TDD, что же мне теперь - на ощупь писать? :) Тесты сами по себе тоже полезны, независимо от TDD.

> Юнит-тест — это тест который помагает разрабатывать лаконичные и правильные внешние интерфейсы

Мы все прекрасно понимаем, что одними интерфейсами разработка не заканчивается - интерфейсы нужно еще реализовать :) И вот во время реализации часто возникает необходимость написать функцию в работе которой не уверен - у меня основной представитель такой неуверенности - ошибка на 1 (особенно в контексте indexOf, substr) и прочие мелочи (например, регулярные выражения) и т.д. и т.п. :)

И я, например, предпочитаю написать пару Unit-тестов, чтобы проверить правильность реализации, а не ждать пока это проявится как баг.

То есть тесты можно использовать не только для TDD и определения архитектуры интерфейсов, но и для повышения собственной уверенности в коде.

Tuesday, February 10, 2009

TDD и покрытие кода тестами

Написал развернутый комментарий на тему "Юнит тесты и TDD" в блог bishop-it.ru, после чего он благополучно обрезался и половина пропала - наверное сработало ограничение на размер. Полный комментарий публику здесь.

Я решил завести отдельный ярлычок "Комментарии" для таких постов, думаю их будет больше одного.

--- Cut ---

Что значит 100% покрытие кода тестами? :)

На первой лекции по тестированию нам привели пример программы из 10 строк кода с двумя вложенными циклами и показали, что чтобы полностью проверить работу этого кода нужно выполнить 10^18 операций :) Я думаю тестировщики знают про это.

TDD - это вовсе не о тестировании и не о покрытии кода тестами. Я вообще не понимаю откуда возникла цифра 100% - я много читал/смотрел/использовал TDD, но никогда не слышал о связи TDD и покрытии кода тестами.

Соглашусь с Андреем Валяевым, что TDD - это подход к разработке, который позволяет разработчикам (а не тестировщикам) облегчить жизнь. И именно по этой же причине, если я правильно понимаю, TDD нравится автору поста.

TDD != тестирование.

Тем более тестирование GUI (и все что касается GUI вообще), которое я вообще считаю шаманством и даже не пытаюсь туда лезть :) Единственное, что я могу понять в тестировании GUI и действительно использую - это ручное тестирование (отредактировать, запустить, проверить UI). Я могу это делать сам (и делаю) или могу отдать специально обученному человеку (и отдаю), который проверяет работу системы по заранее написанным сценариям, составленным по ТЗ.

Unit-тесты позволяют сократить цикл "отредактировать, запустить, проверить", если я тестирую логику - потому что для логики есть API и я могу написать Unit-тест. Особенно если речь идет об утилитарных методах/классах типа парсинга текста, вычисления дат и прочих мелочей.

Типичный случай моего использования TDD - если нужно разработать интерфейс сервиса (или бизнес-логики или API, называйте кому как нравится) и проверить его работу.

Unit-тест - это не интеграционный тест (то есть мы уже не можем получить 100% покрытие функциональности; как я говорил выше есть еще тестирование GUI - также вычтите эту часть из 100%), но основные потоки использования сервиса (его интерфейс + usecase) описать и протестировать можно.

TDD очень часто спасал, когда чесались руки спроектировать супер-пупер универсальную иерархию классов - он просто не дает это сделать, позволяя сосредоточиться на требуемом поведении в рамках четко зафиксированных требований. В качестве таких требований я обычно использую основной поток usecase'а.

TDD позволяет придерживаться принципа KISS и он мне нравится уже только поэтому.

Unit-тест TDD - это тестирование логики как черного ящика, что опять же не дает 100% покрытия.

Одно замечание по поводу "кандалов" тестов. Я никогда не пишу тесты, которые бы мне было жалко удалить и переписать. Не нужно проектировать Unit-тесты - достаточно ограничиться рамками метода теста. В этом плане я считаю хорошей практикой написания Unit-тестов TDD подход Copy-Paste.

P.S.

В начальном посте очень много говорится о 100% покрытия кода тестами, а что означает "покрытие кода" автор не приводит. Я думаю и у тестировщиков (что уж там о девелоперах говорить), одного показателя, который измеряется в процентах - нет.

Да, есть инструменты, которые позволяют измерить Code Coverage (например, Cobertura).

Да, они позволяет получить несколько показателей покрытия, но по сути - это такая же размытая оценка о покрытии тестами кода, как и оценка сложности ПО по размеру кода программы в строках.

Такие методики могут быть полезными при оценке проектов, но никак при разработке, и уж точно ничего не говорят о самой методике TDD.

--- Cut ---