1. Въведение
В тази статия ще разгледаме бързо JBehave, след което ще се съсредоточим върху тестване на REST API от гледна точка на BDD.
2. JBehave и BDD
JBehave е рамка за развитие, управлявана от поведението. Той възнамерява да осигури интуитивен и достъпен начин за автоматизирано тестване за приемане.
Ако не сте запознати с BDD, добре е да започнете с тази статия, обхващаща друга рамка за тестване на BDD - Cucumber, в която представяме общата структура и характеристики на BDD.
Подобно на други BDD рамки, JBehave възприема следните концепции:
- Story - представлява автоматично изпълним прираст на бизнес функционалност, включва един или повече сценарии
- Сценарии - представляват конкретни примери за поведението на системата
- Стъпки - представете действителното поведение, използвайки класически ключови думи BDD: Дадено , кога и тогава
Типичен сценарий би бил:
Given a precondition When an event occurs Then the outcome should be captured
Всяка стъпка в сценария съответства на анотация в JBehave:
- @Given : иницииране на контекста
- @ Кога : направете действието
- @ След това : тествайте очаквания резултат
3. Зависимост на Maven
За да използваме JBehave в нашия проект maven, зависимостта jbehave-core трябва да бъде включена в pom :
org.jbehave jbehave-core 4.1 test
4. Бърз пример
За да използваме JBehave, трябва да следваме следните стъпки:
- Напишете потребителска история
- Сравнете стъпките от потребителската история с Java кода
- Конфигурирайте потребителски истории
- Изпълнете тестове JBehave
- Преглед на резултатите
4.1. История
Нека започнем със следната проста история: „като потребител искам да увелича брояч, за да мога да увелича стойността на брояча с 1“.
Можем да дефинираме историята във файл .story :
Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value
4.2. Стъпки за картографиране
Предвид стъпките, нека внедрим това в Java:
public class IncreaseSteps { private int counter; private int previousValue; @Given("a counter") public void aCounter() { } @Given("the counter has any integral value") public void counterHasAnyIntegralValue() { counter = new Random().nextInt(); previousValue = counter; } @When("the user increases the counter") public void increasesTheCounter() { counter++; } @Then("the value of the counter must be 1 greater than previous value") public void theValueOfTheCounterMustBe1Greater() { assertTrue(1 == counter - previousValue); } }
Не забравяйте, че стойността в поясненията трябва точно да съответства на описанието .
4.3. Конфигуриране на нашата история
За да изпълним стъпките, трябва да подготвим сцената за нашата история:
public class IncreaseStoryLiveTest extends JUnitStories { @Override public Configuration configuration() { return new MostUsefulConfiguration() .useStoryLoader(new LoadFromClasspath(this.getClass())) .useStoryReporterBuilder(new StoryReporterBuilder() .withCodeLocation(codeLocationFromClass(this.getClass())) .withFormats(CONSOLE)); } @Override public InjectableStepsFactory stepsFactory() { return new InstanceStepsFactory(configuration(), new IncreaseSteps()); } @Override protected List storyPaths() { return Arrays.asList("increase.story"); } }
В storyPaths () ние предоставяме нашия .story файлов път, който да бъде анализиран от JBehave. Реалното изпълнение на стъпки е предоставено в stepsFactory () . След това в конфигурацията () , устройството за зареждане на истории и докладът за историята са правилно конфигурирани.
Сега, когато имаме всичко готово, можем да започнем нашата история просто като стартираме: mvn clean test .
4.4. Преглед на резултатите от теста
Можем да видим резултата от теста си в конзолата. Тъй като нашите тестове преминаха успешно, изходът ще бъде същият с нашата история:
Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value
Ако забравим да приложим някоя стъпка от сценария, докладът ще ни уведоми. Кажете, че не сме внедрили стъпката @When :
Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter (PENDING) Then the value of the counter must be 1 greater than previous value (NOT PERFORMED)
@When("the user increases the counter") @Pending public void whenTheUserIncreasesTheCounter() { // PENDING }
В доклада се казва, че @When стъпка е в очакване и поради това стъпката @Then няма да бъде изпълнена.
Ами ако нашата стъпка @Then се провали? Можем да забележим грешката веднага от отчета:
Scenario: when a user increases a counter, its value is increased by 1 Given a counter And the counter has any integral value When the user increases the counter Then the value of the counter must be 1 greater than previous value (FAILED) (java.lang.AssertionError)
5. Тестване на REST API
Сега разбрахме основите на JBhave ; ще видим как да тестваме REST API с него. Нашите тестове ще се основават на предишната ни статия, обсъждаща как да тестваме REST API с Java.
В тази статия тествахме API на GitHub REST и се фокусирахме главно върху HTTP кода за отговор, заглавките и полезния товар. За простота можем да ги запишем съответно в три отделни истории.
5.1. Тестване на кода на състоянието
Историята:
Scenario: when a user checks a non-existent user on github, github would respond 'not found' Given github user profile api And a random non-existent username When I look for the random user via the api Then github respond: 404 not found When I look for eugenp1 via the api Then github respond: 404 not found When I look for eugenp2 via the api Then github respond: 404 not found
Стъпките:
public class GithubUserNotFoundSteps { private String api; private String nonExistentUser; private int githubResponseCode; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @Given("a random non-existent username") public void givenANonexistentUsername() { nonExistentUser = randomAlphabetic(8); } @When("I look for the random user via the api") public void whenILookForTheUserViaTheApi() throws IOException { githubResponseCode = getGithubUserProfile(api, nonExistentUser) .getStatusLine() .getStatusCode(); } @When("I look for $user via the api") public void whenILookForSomeNonExistentUserViaTheApi( String user) throws IOException { githubResponseCode = getGithubUserProfile(api, user) .getStatusLine() .getStatusCode(); } @Then("github respond: 404 not found") public void thenGithubRespond404NotFound() { assertTrue(SC_NOT_FOUND == githubResponseCode); } //... }
Забележете как при изпълнението на стъпките използвахме функцията за инжектиране на параметри . Аргументите, извлечени от кандидата за стъпка, просто се съчетават, следвайки естествения ред на параметрите в анотирания Java метод.
Също така се поддържат анотирани поименни параметри:
@When("I look for $username via the api") public void whenILookForSomeNonExistentUserViaTheApi( @Named("username") String user) throws IOException
5.2. Тестване на типа носител
Ето една проста история за тестване на MIME тип:
Scenario: when a user checks a valid user's profile on github, github would respond json data Given github user profile api And a valid username When I look for the user via the api Then github respond data of type json
И ето стъпките:
public class GithubUserResponseMediaTypeSteps { private String api; private String validUser; private String mediaType; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @Given("a valid username") public void givenAValidUsername() { validUser = "eugenp"; } @When("I look for the user via the api") public void whenILookForTheUserViaTheApi() throws IOException { mediaType = ContentType .getOrDefault(getGithubUserProfile(api, validUser).getEntity()) .getMimeType(); } @Then("github respond data of type json") public void thenGithubRespondDataOfTypeJson() { assertEquals("application/json", mediaType); } }
5.3. Тестване на полезния товар на JSON
Тогава последната история:
Scenario: when a user checks a valid user's profile on github, github's response json should include a login payload with the same username Given github user profile api When I look for eugenp via the api Then github's response contains a 'login' payload same as eugenp
И изпълнението на обикновените прави стъпки:
public class GithubUserResponsePayloadSteps { private String api; private GitHubUser resource; @Given("github user profile api") public void givenGithubUserProfileApi() { api = "//api.github.com/users/%s"; } @When("I look for $user via the api") public void whenILookForEugenpViaTheApi(String user) throws IOException { HttpResponse httpResponse = getGithubUserProfile(api, user); resource = RetrieveUtil.retrieveResourceFromResponse(httpResponse, GitHubUser.class); } @Then("github's response contains a 'login' payload same as $username") public void thenGithubsResponseContainsAloginPayloadSameAsEugenp(String username) { assertThat(username, Matchers.is(resource.getLogin())); } }
6. Обобщение
В тази статия представихме накратко JBehave и внедрихме тестове за REST API в стил BDD.
В сравнение с нашия обикновен тестов код на Java, кодът, реализиран с JBehave, изглежда много ясен и интуитивен, а отчетът за резултатите от теста изглежда много по-елегантен.
Както винаги, примерният код може да бъде намерен в проекта Github.