O introducere în dezvoltarea bazată pe comportament în R

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

(Acest articol a fost publicat pentru prima dată pe Jakub :: Sobolewskiși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)


Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.

Când dezvoltați software, o deconectare între ceea ce doresc utilizatorii și ceea ce face software -ul. S -ar putea să fi livrat cod de lucru, testat, dar rezolvă problema utilizatorului?

Dezvoltarea bazată pe comportament își propune să atenueze acel risc, prin captarea și testarea cerințelor din perspectiva utilizatorului extern al sistemului.

Cum funcționează BDD

Indiferent dacă lucrăm cu un proprietar de produs, cu utilizatori sau de la noi înșine, BDD ne poate ajuta să explorăm și să definim problema într -un mod structurat.

Procesul este următorul:

  1. Cercetăm o dorință vagă cu o poveste de utilizator.
  2. Refinim povestea utilizatorului în exemple. Aceste exemple descriu cum putem spune dacă dorința a fost îndeplinită. Concentrează -te pe ceea ce vrei să obții, nu pe cum să -l realizezi.
  3. Creăm specificații. Sunt traduceri directe ale exemplelor în cod.

Acest lucru ne ajută să trecem de la o descriere vagă la o specificație testabilă foarte precisă.

Captarea unei dorințe cu o poveste de utilizator

Să ne imaginăm că vrem să implementăm o librărie. Prima poveste a utilizatorului ar putea fi:

În calitate de client, vreau să selectez o carte și să o adaug în coș, astfel încât să o pot cumpăra.

Rafinarea poveștii utilizatorului în exemple

Dezvoltarea bazată pe comportament ne ajută să ne concentrăm pe comportament, folosind un limbaj care exprimă comportament:

  • Dat un anumit context,
  • Când apare un eveniment,
  • Apoi Un rezultat trebuie observat.

În acest exemplu, am putea scrie:

  • Având în vedere că sunt în librărie
  • Când selectez „The Hobbit, Jrr Tolkien”
  • Când adaug carte selectată în coș
  • Atunci ar trebui să văd „The Hobbit, Jrr Tolkien” în căruță

Această descriere este mai precisă decât povestea utilizatorului. Acesta descrie ce trebuie să se întâmple, ce trebuie să facă utilizatorul și ce rezultat ar trebui să vadă utilizatorul.

La acest nivel, nu știm nimic despre implementarea librăriei. Această descriere se potrivește oricărei implementări a librăriei:

  • ar putea fi o aplicație web,
  • ar putea fi o aplicație CLI,
  • Sau ar putea fi un magazin fizic cu un asistent robot.

Implementarea poate fi modificată în orice moment, iar executarea specificației ar trebui să ne spună dacă sistemul permite utilizatorului să își atingă obiectivul.

Implementarea specificațiilor executabile

Specificațiile implementate cu dezvoltarea bazată pe comportament sunt:

  • lizibil instantaneu,
  • concentrat pe obiectivul de afaceri,
  • ascunderea detaliilor implementării,
  • Încurajarea reutilizării codului de testare.

Dezvoltarea bazată pe comportament nu se referă la instrumente. Este un mod de a construi software. Putem implementa specificații în orice mod dorim, cu toate acestea, există instrumente care ne pot ajuta. {castravete} este unul dintre ele.

BDD cu baza r

Putem practica BDD folosind BASE R. Având un set de exemple despre modul în care ar trebui să funcționeze sistemul, trebuie să stabilim o modalitate de a traduce exemple în cod.

Trebuie să reprezentăm limba de afaceri cu cod – un set de acțiuni pe care le poate întreprinde utilizatorul sistemului. O putem face cu funcții. Aceste funcții vor crea interfața sistemului. Numele lor pot urma îndeaproape descrierea limbajului natural a acțiunii. Aceștia vor rezuma și vor ascunde detaliile de implementare ale sistemului. Acestea vor permite reutilizarea codului de testare.

Specificația librăriei ar putea arăta astfel:

# tests/acceptance/test-bookstore.R
test_that("Bookstore: Adding a book to cart", {
  # Given
  bookstore <- Bookstore$new()
  # When
  bookstore$select("The Hobbit, J.R.R. Tolkien")
  bookstore$add_to_cart()
  # Then
  bookstore$cart_includes("The Hobbit, J.R.R. Tolkien")
})

Acest test nu reușește, deoarece codul nu există încă. Putem face o pauză în acest pas și să încercăm câteva versiuni ale specificațiilor și să mergem cu una care exprimă cel mai bine obiectivul de afaceri.

Odată ce avem o prezentare a acțiunilor, le putem implementa. Putem folosi obiecte R6 pentru a lega funcțiile împreună.

# tests/acceptance/setup-bookstore.R
Bookstore <- R6::R6Class(
  public = list(
    select = function(title) {

    },
    add_to_cart = function() {

    },
    cart_includes = function(title) {

    }
  )
)

Când avem un schelet al implementării, putem începe să -l umplem cu codul real.

De exemplu, implementarea noastră a librăriei ar putea fi un pachet cu:

  • select_book: funcție care returnează un tibble cu detalii despre carte,
  • add_to_cart: funcție care adaugă o carte cu un ID dat la un depozit,
  • get_cart: funcție care returnează o listă cu detalii despre cărți din coș.

O implementare care satisface specificația ar putea fi la fel de simplă:

storage <- c()
books <- list(
  tibble::tibble(id = 1, title = "The Hobbit, J.R.R. Tolkien"),
  tibble::tibble(id = 2, title = "The Lord of the Rings, J.R.R. Tolkien")
)

select_book <- function(title) {
  books |>
    purrr::keep((x) x$title == title) |>
    purrr::pluck(1)
}

add_to_cart <- function(id) {
  storage <<- c(storage, id)
}

get_cart <- function() {
  books |>
    purrr::keep((x) x$id %in% storage)
}

Având această implementare, o putem conecta la codul de testare:

# tests/acceptance/setup-bookstore.R
Bookstore <- R6::R6Class(
  private = list(
    selected_id = NULL
  ),
  public = list(
    select = function(title) {
      private$selected_id <- select_book(title)$id
    },
    add_to_cart = function() {
      add_to_cart(private$selected_id)
    },
    cart_includes = function(title) {
      testthat::expect_in(title, purrr::map_chr(get_cart(), "title"))
    }
  )
)

Acum, testele trec.

# tests/acceptance/test-bookstore.R
test_that("Bookstore: Adding a book to cart", {
  # Given
  bookstore <- Bookstore$new()
  # When
  bookstore$select("The Hobbit, J.R.R. Tolkien")
  bookstore$add_to_cart()
  # Then
  bookstore$cart_includes("The Hobbit, J.R.R. Tolkien")
})

Cu această implementare, putem extinde cu ușurință testele cu verificarea dacă putem adăuga mai multe cărți în coș.

test_that("Bookstore: Adding multiple books to cart", {
  # Given
  bookstore <- Bookstore$new()
  # When
  bookstore$select("The Hobbit, J.R.R. Tolkien")
  bookstore$add_to_cart()
  bookstore$select("The Lord of the Rings, J.R.R. Tolkien")
  bookstore$add_to_cart()
  # Then
  bookstore$cart_includes(c("The Hobbit, J.R.R. Tolkien", "The Lord of the Rings, J.R.R. Tolkien"))
})

Pe măsură ce sistemul crește, acesta va fi extins cu mai multe exemple și mai multe acțiuni. Acțiunile deja implementate vor fi reutilizate în diferite scenarii.

Implementarea sistemului va evolua, la fel și specificațiile, dar această abordare va asigura că codul de testare este ușor de întreținut și se concentrează asupra obiectivului de afaceri.

Bdd cu {castravete}

Pașii BDD cu {castravete} sunt aceiași ca în cazul bazei R. Diferența în modul în care exprimăm specificațiile și implementarea acestora.

Leabilitate a specificațiilor este dată de aceștia fiind exprimați în limbajul Gherkin. Specificațiile nu mai sunt exprimate în cod, ci ca text. Adăugă un alt nivel de separare între specificație și implementare.

Începem prin a scrie un fișier de funcții:

# tests/acceptance/bookstore.feature
Feature: Bookstore
  Scenario: Adding a book to cart
    Given I am in the bookstore
    When I select "The Hobbit, J.R.R. Tolkien"
    When I add selected book to the cart
    Then I should see "The Hobbit, J.R.R. Tolkien" in the cart

Implementăm acțiuni cu given, whenși then Funcții:

# tests/acceptance/setup-steps.R
given("I am in the bookstore", function(context) {
})

when("I select {string}", function(title, context) {
  context$selected_id <- select_book(title)$id
})

when("I add selected book to the cart", function(context) {
  add_to_cart(context$selected_id)
})

then("I should see {string} in the cart", function(title, context) {
  expect_in(title, purrr::map_chr(get_cart(), "title"))
})

Acest test face exact același lucru cu testul cu baza R.

Putem efectua aceste teste cu:

cucumber::test()
#> Test passed

Ce cucumber::test Funcția face este că citește fișierele de caracteristici, găsește implementări de acțiuni corespunzătoare și le rulează în ordine. Pentru a afla mai multe cum funcționează, consultați modul în care funcționează vinie.

Similar cu ceea ce am făcut cu baza R, putem extinde fișierul cu un scenariu care verifică dacă putem adăuga mai multe cărți în coș:

# tests/acceptance/bookstore.feature
Feature: Bookstore
  Scenario: Adding a book to cart
    Given I am in the bookstore
    When I select "The Hobbit, J.R.R. Tolkien"
    When I add selected book to the cart
    Then I should see "The Hobbit, J.R.R. Tolkien" in the cart

  Scenario: Adding multiple books to cart
    Given I am in the bookstore
    When I select "The Hobbit, J.R.R. Tolkien"
    When I add selected book to the cart
    When I select "The Lord of the Rings, J.R.R. Tolkien"
    When I add selected book to the cart
    Then I should see "The Hobbit, J.R.R. Tolkien" in the cart
    Then I should see "The Lord of the Rings, J.R.R. Tolkien" in the cart

Re-rularea testelor va duce la trecerea a două scenarii.

cucumber::test()
#> Test passed
#> Test passed

De ce ar trebui să alegeți {castravete}?

  • Vă permite să începeți să practicați BDD fără a fi nevoie să vă dați seama cum să lipiți implementările de acțiuni pe cont propriu.
  • Vă permite să exprimați teste la nivel înalt într-un limbaj natural.
  • Vă ajută să păstrați separarea între specificație și implementare.
  • Vă ajută să extindeți și să reutilizați codul de testare.

Învățarea BDD

  • ATDD prin exemplu: un ghid practic pentru acceptarea dezvoltării bazate pe teste: un ghid practic pentru acceptarea dezvoltării bazate pe teste.
  • Castravete

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.