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.

Designing REST API automation tests in Kotlin


Published on

XP days 2017 talk

Published in: Software
  • Be the first to comment

Designing REST API automation tests in Kotlin

  1. 1. Daimler Mobility Services GmbH Designing of REST API automation tests in Kotlin Mercedes pay S.A.
  2. 2. Dmitriy Sobko Senior Automation QA Engineer Mercedes pay Ciklum Mercedes pay S.A.
  3. 3. Agenda 1. Microservices architecture 2. REST API 3. Back-end testing principles 4. Kotlin 5. Cucumber framework 6. Spring Boot framework 7. Putting it all together!!! Mercedes pay S.A.
  4. 4. Microservices
  5. 5. REST API
  6. 6. SADNESS
  7. 7. Testing principles Three basic principles: ▸Make each test self-sufficient and independent ▸Rely on contract -> not implementation ▸Use Domain-driven design
  8. 8. And tips ▸Don’t hesitate to use DB/cache, etc., if needed ▸Know and use at least basic code style principles ▸Don’t reinvent the wheel And yes, it is possible to make test code more clean and better than application code!
  9. 9. Don’t forget about test reports!
  10. 10. Let’s imagine a typical finance IT project!
  11. 11. What do we want to achieve? Tests should be: ▸Stable ▸Resistant to constant application code changes ▸Fast ▸Extensible ▸Easily supported
  12. 12. Back to the basis
  13. 13. Kotlin
  14. 14. How it looks like in real code? class PersonWithBuiltInStdLib(val name: String, age: Int, salary: Int) : PropertyChangeAware() { private val observer = { prop: KProperty<*>, oldValue: Int, newValue: Int -> changeSupport.firePropertyChange(, oldValue, newValue) } var age: Int by Delegates.observable(age, observer) var salary: Int by Delegates.observable(salary, observer) }
  15. 15. Why Kotlin? ▸Keep calm! The syntax is really lean and intuitive! ▸Compiles to JVM bytecode. And not only! ▸Statically-typed ▸Functional or OOP style
  16. 16. Null-aware type system val value: String = "Clean Code" val value: String = null // compile error! Can't assign null to non-null type. val nullableValue: String? = "Clean Code" val nullableValue: String? = null
  17. 17. String interpolation val i = 10 val s = "i = $i" // evaluates to "i = 10" val s = "abc" val str = "$s.length is ${s.length}" // evaluates to "abc.length is 3"
  18. 18. Data classes data class DesignData(val id: Int, val fileName: String, val uploaderId: Int, val width: Int = 0, val height: Int = 0) val design = DesignData(id = 1, fileName = "cat.jpg", uploaderId = 2)
  19. 19. Extension functions fun String.unmarshallToPayment(): Payment { val a = this.toByteArray() return JAXBContext.instance.unmarshal(a.soapBody()) as Payment } fun Int.convertSecToMillis(): Long = (this * 1000).toLong()
  20. 20. Object classes object OauthTokenGenerator { fun generateAuthToken(username: String, password: String) = "Basic ${Base64.getEncoder() .encodeToString("$username:$password".toByteArray())}“ }
  21. 21. Second look 180 lines of Java code 60 lines of Kotlin code
  22. 22.!t=jo&jid=/google/kotlin-software-engineer-android-1600-amphitheatre- pkwy-mountain-view-ca-2643700480
  23. 23. What to use for our automation testing framework?
  24. 24. BDD
  25. 25. Cucumber
  26. 26. Cucumber scenario Feature: Payments @TMS-9990 @Ready Scenario: Customer successfully completes payment with CC Given Customer #1 pays with CC to merchant #1 10.0 EUR Then assert payment response status: 201 And assert there are 1 PROCESSED payments for remote owner And assert customer account balance is changed And assert merchant account balance is changed
  27. 27. Why Cucumber? ▸Bridge between the business language and the code ▸New tests without coding skills ▸Truly end-to-end test framework ▸Possibility to simply reuse the code
  28. 28. Spring Boot
  29. 29. Why Spring Boot? ▸Test scenario “session” -> Spring context ▸Each test as stand-alone application ▸Extremely easy to configure any SQL/NoSQL DB connections ▸All beauty and power of Spring
  30. 30. “The first rule of any technology used in a business is that automation applied to an efficient operation will magnify the efficiency. The second is that automation applied to an inefficient operation will magnify the inefficiency.“ Bill Gates
  31. 31. Putting it all together!!!
  32. 32. @Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) @Retention(AnnotationRetention.RUNTIME) @MustBeDocumented @Inherited @SpringBootTest @ContextConfiguration(classes = arrayOf(MainConfiguration::class)) @DirtiesContext annotation class CucumberStep Step annotation
  33. 33. @CucumberStep class PaymentSteps (private val payService: PaymentService) { @Given("^Customer #(d+) pays with (CC|SEPA) to merchant #(d+) (.+) (EUR|USD|CNY)$") fun executePayment(customer: Int, payment: Payment, merchant: Int, amount: BigDecimal, currency: String) { payService.executePayment(customer, payment, merchant, amount, currency) } } Steps class
  34. 34. @RunWith(Courgette::class) @CourgetteOptions(threads = 4, runLevel = CourgetteRunLevel.FEATURE, rerunFailedScenarios = false, cucumberOptions = CucumberOptions(features = arrayOf("resources/features"), glue = arrayOf("com.dsobko.test"), tags = arrayOf("@Ready", "~@Bug"), plugin = arrayOf("pretty", "html:build/cucumber-report"))) object CucumberFeaturesRunner Cucumber runner
  35. 35. @Configuration @ComponentScan(basePackageClasses = arrayOf( @EnableAutoConfiguration class MainConfiguration Configuration class
  36. 36. logging: level: org.springframework.web: 'INFO' spring: datasource: driver-class-name: org.postgresql.Driver url: ${DB_URL:jdbc:postgresql://} username: ${DB_USER:postgres} password: ${DB_PASSWORD:system} main: service: host: ${HOST:} application.yml
  37. 37. @Component class AccountRepository(private val repo: MainRepository) { private val SELECT_BALANCE = "SELECT balance FROM account WHERE uuid = {0}" fun retrieveBalance(uuid: String): BigDecimal { val query = MessageFormat.format(SELECT_BALANCE, uuid) val balance = repo.executeSelectQuery(query)"Account $uuid balance = $balance") return balance } } Working with PostgreSQL
  38. 38. @Repository interface PaymentRepository: MongoRepository<PaymentDoc, String> { @Query(value = "{ 'walletId' : ?0 }") fun findByWalletId(walletId: String): PaymentDoc } Working with MongoDB
  39. 39. @Component class SharedContext { var merchant: Int = 1 var customer: Int = 1 var customersData: MutableMap<Int, Customer> = HashMap() var currency: String = "EUR" var amount: BigDecimal = TEN } Context class
  40. 40. data class PaymentResponse(val uuid: String, val amount: BigDecimal, val currency: String, val createdAt: Date) { var extraData: List<ExtraData>? = null } data class ExtraData(val referenceNumber: String) Requests’ and responses’ models
  41. 41. @Service class PaymentService() { fun executeRequest(): Any? { val request = requestBuilder.createPaymentRequest() return given() .log().all() .accept(JSON).contentType(JSON) .body(request) .post(configs.paymentPath()) .log().all() .extract() } } Service class
  42. 42. What we have achieved ▸ Tests are stable (Cucumber) ▸ Tests are really resistant to application code changes (Cucumber, Spring Boot) ▸ They are extremely fast (Cucumber, Kotlin, Spring Boot) ▸ And Extensible (Kotlin) ▸ And, of course, easily supported (Kotlin, Spring Boot)
  43. 43. And even more!!! ▸ Each test is human-readable and test report can be shown to any non-technical person ▸ Adding new DB is really not a big deal ▸ No technical limitations to cover the most tricky and non-trivial test scenarios ▸ Extremely easy to create new scenarios without any code changes Test code looks better and more simple than application code!!!
  44. 44. Daimler Mobility Services GmbH Mercedes pay S.A.