Рецензия на JBehave

:

Резюме: в последнее время все большей популярностью начинают пользоваться BDD фреймворки для тестирования, однако не каждый из них так хорош как кажется.

О BDD тестах

Сначала поясню что такое BDD (Behaviour Driven Development) и какая идея стоит за BDD тестами. Так вот, требования можно записывать по-разному, и одной из форм является Given-When-Then стиль, например:

Given User is registered and logged in, when she posts an article, article count gets incremented.

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

Как JBehave реализует BDD тесты

Сначала описывается сценарий в простом текстовом файле называемом Историей:

Scenario: When user write an article, her article count increments

Given user is registered
And user is logged in
When user posts an article
Then user article count is incremented

Каждый из этих шагов мапится на метод в так называемых Step’ах в Java классах:

@Given("user is registered")
public void userIsRegistered(){}

@Given("user is logged in")
public void userIsLoggedIn(){}

@When("user posts an article")
public void writeAnArticle() {}

@Then("user article count is incremented")
public void assertArticleCountIncremented(){}

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

Проблемы JBehave

Неожиданно, когда начинаешь сам писать тесты, сталкиваешься с двумя проблемами, о которых сначала и не думал.

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

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

В общем это достаточно роковые недостатки из-за которых я разочаровался в JBehave. Особенно учитывая тот факт, что фреймворк в последнее время практически не развивается.

Альтернативы

Сначала стоит заметить что на голом TestNG тесты выходят намного компактней и приятней, хоть и об отчетах приходится думать “вручную”.

А еще есть с первого взгляда вкусные фреймворки, которые лишены перечисленных недостатков, такие как основанный на Groovy easyb:

scenario "null is pushed onto empty stack", {
  given "an empty stack",{
    stack = new Stack()
  }

  when "null is pushed", {
    pushnull = {
      stack.push(null)
    }
  }

  then "an exception should be thrown", {
    ensureThrows(RuntimeException){
      pushnull()
    }
  }

  and "then the stack should still be empty", {
    stack.empty.shouldBe true
  }
}

Снова Groovy-based фреймворк Spock:

def "HashMap accepts null key"() {
  setup:
  def map = new HashMap()
  
  when:
  map.put(null, "elem")
  
  then:
  notThrown(NullPointerException)
}

Сам я с этими фреймворками не работал и не знаю их недостатков, но как минимум у них есть test case scope.