Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Slawek Kluz - Ewolucja modelu danych w testach funkcjonalnych – case study

703 views

Published on

Slawek Kluz - Ewolucja modelu danych w testach funkcjonalnych – case study

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Slawek Kluz - Ewolucja modelu danych w testach funkcjonalnych – case study

  1. 1. Ewolucja modelu danych w testach funkcjonalnych - case study Sławomir Kluz
  2. 2. Architektura - mikroserwisy - rest/json - event procesory - zewnętrzni dostawcy - aplikacje klienckie - CI/CD - ~ 40 serwerów microservice 01 microservice 03 microservice 02 microservice 05 microservice 06 microservice 04 microservice 07 postgresql apache kafka couchbase redis microservice 08 website backoffice proxy
  3. 3. Testy ● API: spójność danych, szybki feedback, wpływ na integracje + CD ● website/backoffice - selenium ● testy wydajnościwe API
  4. 4. Wersja 1.0 ● start projektu - testy API ● gatling jako framework testowy (testy funkcjonalne + wydajnościowe) ● model danych ○ tekst/mapy
  5. 5. object Account { val fullBody = """{ "password": "${password}", "email": "${email}", "nickName": "${nickName}", "birthDate": "${birthDate}", "phone": "${phone}", "street": "${street}" }""" val updateBody = """{ "id":${accountId}, "password": "${password}", "email": "${email}", "nickName": "${nickName}", "birthDate": "${birthDate}", "phone": "${phone}", "street": "${street}" "revision": ${accountRevision} }""" val incompleteBodyMissingEmail = """{ "password": "${password}", "nickName": "${nickName}", "birthDate": "${birthDate}", "phone": "${phone}", "street": "${street}" }""" }
  6. 6. Wersja 1.0 ● start projektu - testy API ● gatling jako framework testowy (testy funkcjonalne + wydajnościowe) ● model danych ○ tekst/mapy ○ json wrapper
  7. 7. object ParticipantDataFeeder { private val dataString = """{ |"name":"Arsenal", |"sport": { | "id" : "football", | "name" : "Football" |}, |"metadata":{ | "name":"Arsenal", | "type":"team" | } |}""".stripMargin def correctData = dataString def updateWithoutMetadata = new jsonBody (dataString) .remove("metadata").get def withName(name: String) = new jsonBody (dataString) .set("name", name).get def withSport(sport: String) = new jsonBody (dataString) .setJson("sport", sport).get def withoutMetadata = new jsonBody(dataString) .remove("metadata").get }
  8. 8. private val testScenario: PopulatedScenarioBuilder = scenario(getClass.getSimpleName) .exec(Session.rootAuthenticationToken(Some(testUniverse)) .exec(Participant.put("spin","star",UUID.randomUUID().toString, ParticipantDataFeeder.withoutMetadata, status.in(200,201), jsonPath("$.metadata.name").notExists, jsonPath("$.metadata.type").notExists )) ).inject(performanceStrategy)
  9. 9. Wersja 1.0 ● zalety ○ szybkość tworzenia ○ możliwość użycia w innych narzędziach ○ zrozumiałe dla nietechnicznej osoby ● problemy ○ ciężkie do utrzymania przy zmieniającym się modelu, powiązaniu danych i dużej ilości testów ○ ciężkie do użycia poza frameworkiem testowym
  10. 10. Nowe wymagania ● powstają aplikacje webowe ○ test są dość wolne - problem przy CI/CD ○ nie wszystkie dane da się stworzyć z użyciem strony internetowej ○ użycie API do przygotowania danych ● narzędzia ○ potrzeba narzędzi do analizy danych ● problemy z frameworkiem testowym ○ dość wolne wdrożenie ○ brak wsparcia IDE
  11. 11. Wersja 2.0 ● struktura kodu projektu - sbt
  12. 12. Wersja 2.0 ● zmiana frameworka testowego ● tests-model ○ proste klasy opisujące model danych request/response nie związane z żadnym frameworkiem testowym ○ zależności pomiędzy obiektami ○ samplery: przykładowe obiekty w danym stanie
  13. 13. case class ChatMessage( `type`: String, content: MessageContent, sender: Option[AccountReference] = None, tags: Option[Array[String]] = None, sentAt: Option[String] = None ) case class AccountReference(id: Int, `type`: String, name: String) case class MessageContent(text: String)
  14. 14. object PaymentMethodSampler { def creditCardPayment = PaymentMethod( provider = Some("realex"), `type` = Some("credit-card") ) def invalidProviderPayment = PaymentMethod( provider = Some("polcard"), `type` = Some("credit-card") )
  15. 15. Wersja 2.0 ● tests-api ○ serializacja/deserializacja obiektów modelu ○ wbudowany mechanizm z rest-assured ○ warstwa “serwisów”
  16. 16. object ChatService { val _sendMessage = " /chats/{universe}/{chatType}/{chatId}/messages" def sendMessage(session: Session, universe: String, chatType: String, chatId: String, message: AnyRef) = { given() .specification(Specifications.authorizedExternal(session)) .pathParam("universe", universe) .pathParam("chatType", chatType) .pathParam("chatId", chatId) .body(message) .when() .post(_sendMessage) .Then() .assertThat() .statusCode(200) } }
  17. 17. def chatWithHistory(session: Session, universe: String, chatType: String, chatId: String): ChatHistory = { given() .specification(Specifications.authorizedExternal(session)) .pathParam("universe", universe) .pathParam("chatType", chatType) .pathParam("chatId", chatId) .when() .get(_chatWithHistory) .Then() .assertThat() .statusCode(200) .extract().body().as(classOf[ChatHistory]) }
  18. 18. var history = ChatService.chatWithHistory(staffSession, universeId, customerAccount.`type`.get, chatId) assert(history.id == chatId, "ChatHistory id should be equal customerId") assert(history.messages.size == 5, "Five messages should be created") assert(history.participants.size == 2, "Two participants should be included in chat") inside (history.messages.head) { case ChatMessage(_type, content, sender, tags, sentAt) => _type should be ("something") sentAt.get should startWith("prefix") tags should contain("one", "two") }
  19. 19. Wersja 2.0 ● tests-web ○ możliwość użycia warstwy serwisów do przygotowania danych ○ możliwość sprawdzenia stanu danych (eventy) ○ asercje z użyciem obiektów modelu
  20. 20. class DisplayEventTest extends BaseSuite { behavior of "Events page" it should "display event with proper data" taggedAs Smoke in { val (operatorEvent, operatorMarket) = EventProvider.createStartFootballMatchWinnerEvent() Backoffice.open().login() val tradingPage = Backoffice.openTradingPage() val eventPage = tradingPage.openEventPage(operatorEvent.id.get) operatorEvent.name shouldBe eventPage.eventName.getText operatorEvent.id.get shouldBe eventPage.eventId.getText operatorEvent.display.get shouldBe eventPage.isEventShow() operatorEvent.active.get shouldBe eventPage.isEventActive() operatorEvent.timeSettings.get.startTime.substring(0,10) shouldBe eventPage.startTime.getText. substring(0,10) } }
  21. 21. Wersja 2.0 ● zalety ○ łatwość wprowadzania zmian/utrzymania kodu ○ czytelność kodu ○ szybkie testy przeglądarkowe ○ tworzenie zewnętrznych narzędzi z użyciem serwisów ○ sterowanie mechanizmem serializacji ○ wsparcie IDE ● wady ○ użycie danych niezgodnych z modelem ○ problemy z serializacją
  22. 22. Pytania

×