Статьи

CoffeeScript: пример TDD

CoffeeScript — это язык, создающий абстракцию над JavaScript (как предполагает подобное имя). Это абстракция над синтаксисом JavaScript, а не над его концепциями: язык по-прежнему основан на функциях как объектах, которые могут связываться с другими объектами, и прототипах. наследование.

CoffeeScript поддерживает лучшие практики JavaScript, превращая абстракции, которые вы в любом случае написали бы или позаимствовали из фреймворка, в концепции языка для максимальной краткости. Он имеет этап компиляции — как и любой язык, который должен компилироваться в более низкий уровень, такой как C или Java.

Поскольку ковбойское кодирование не является моим предпочтительным способом работы, я подготовил пример разработки через тестирование с помощью jsTestDriver . В этой статье вы получите две вещи: введение в CoffeeScript и инструменты для его модульного тестирования (и, следовательно, как использовать TDD с CoffeeScript).

Инфраструктура

Базовая структура состоит из двух папок: src / и lib /; помните шаг компиляции. Мы поместим файлы .coffee в src / и скомпилируем их в эквиваленты .js в симметричном дереве в lib /.

Мы также добавляем файл jsTestDriver.conf, чтобы сообщить инфраструктуре модульного тестирования все загружаемые файлы, которые являются только «двоичными» сценариями .js:

server: http://localhost:4224

load:
  - lib/*.js

составление

Это первая версия теста, которую мне удалось написать, fizzbuzztest.coffee. Это тавтология, которая всегда должна проходить:

mytest = () -> assertEquals(1, 1)

tests = {
  "test1is1": mytest
}

TestCase("tests for fizzbuzz kata", tests)

Здесь вы видите, что функции все еще являются объектами первого класса, но поддерживаются только анонимные функции. CoffeeScript — это Python / Ruby-подобный язык без точек с запятой, и с последним языком есть некоторые сходства и общие черты.

Я до сих пор использую старый синтаксис для вызова функций, хотя во многих случаях скобки могут быть опущены. CoffeeScript консервативен и даже принимает точки с запятой, если вы хотите написать их.

Я скомпилировал этот скрипт с кофе -o lib / -c src /. fizzbuzztest.js является результатом:

(function(){
  var mytest, tests;
  mytest = function() {
    return assertEquals(1, 1);
  };
  tests = {
    "test1is1": mytest
  };
  TestCase("tests for fizzbuzz kata", tests);
})();

По умолчанию глобальное пространство имен не затрагивается, и
ключевые слова
var автоматически вводятся для его сохранения. Когда мне позже понадобилось глобальное пространство имен, я написал:

this.fizzbuzz = /* ... function definition ... */

это в данном случае является объект окна или другим глобальным объектом , где вы исполняете код.

Бег

Чтобы запустить тест, мы должны инициализировать тестовый драйвер (только один раз):

jsTestDriver java -jar JsTestDriver-1.3.2.jar --port 4224

jsTestDriver теперь будет слушать на локальном хосте: 4224. Загрузите этот URL в ваш браузер и перехватите его, нажав на ссылку. По запросу тесты будут выполняться внутри браузера: более подробную информацию см. В соответствующей статье .

Каждый раз, когда вы хотите запустить тесты, выполняйте из командной строки:

java -jar JsTestDriver-1.3.2.jar --tests all

Это полная история моего ката . Вот окончательная версия кода (предупреждение о спойлере!), С поддержкой добавления других факторов, кроме 3 или 5. Код, вероятно, уродливее среднего, но он компилируется:

tests = {
  "test ordinary numbers are unchanged": ->
    assertEquals(1, fizzbuzz(1))
    assertEquals(2, fizzbuzz(2))
    assertEquals(4, fizzbuzz(4))
    assertEquals(142, fizzbuzz(142))
  "test multiples of 3 become fizz": ->
    assertEquals("Fizz", fizzbuzz(3))
    assertEquals("Fizz", fizzbuzz(6))
    assertEquals("Fizz", fizzbuzz(9))
  "test multiples of 5 become buzz": ->
    assertEquals("Buzz", fizzbuzz(5))
    assertEquals("Buzz", fizzbuzz(10))
  "test multiples of 3 and 5 become fizzbuzz": ->
    assertEquals("FizzBuzz", fizzbuzz(15))
    assertEquals("FizzBuzz", fizzbuzz(45))
}

TestCase("tests for fizzbuzz kata", tests)
newRule = (word, divisor) ->
  (number) ->
    return word if number % divisor == 0
    ""

newFizzBuzz = (rules) ->
  (number) ->
    result = ""
    concatenation = (rule) ->
      result = result + rule(number)
    concatenation rule for rule in rules
    return result if result
    number

fizzRule = newRule("Fizz", 3)
buzzRule = newRule("Buzz", 5)

this.fizzbuzz = newFizzBuzz([fizzRule, buzzRule])

Комментарии к опыту

CoffeeScript предлагает более короткий синтаксис , который представляет собой некоторую кривую обучения, но не крутой . Я прошел весь пример за 1 час (однако я уже знал, как использовать jsTestDriver.)

Синтаксис формирует способ написания кода, упрощая некоторые вещи: я обнаружил, что использую функцию более высокого порядка, которая чаще создает другие , поскольку создание функции сейчас — это просто вопрос -> перед некоторыми строками кода.

Присвоение имен переменным также проще, поскольку вам нужно думать только об имени, а не о var или загрязнении области видимости. Больше времени уделять дизайну, а не языковым проблемам. Некоторые из них, такие как инструкция, если выражение удобны, но не обязательны, и есть благодаря вдохновению Ruby.

В CoffeeScript есть еще кое-что, например, опции для связывания функций, которые помогают не потерять ссылку на это. Однако вопрос заключается в том, имеет ли все это удобство большую ценность, чем время, затрачиваемое на изучение нового языка и добавление инфраструктуры для его работы — компилятор, хуки сборки и параллельное дерево, которое нужно игнорировать в вашей системе управления версиями.