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.
Upcoming SlideShare
Design patterns in test automation
Next
Download to read offline and view in fullscreen.

Share

Java → kotlin: Tests Made Simple

Download to read offline

Heisenbug St. Petersburg, 04.06.2017

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all

Java → kotlin: Tests Made Simple

  1. 1. Java → Kotlin: Tests Made Simple — Leonid Rudenko
  2. 2. _2 Why? —
  3. 3. _3 Why? —
  4. 4. • Reference http://kotlinlang.org/docs/reference/ _5 Useful links —
  5. 5. • Reference http://kotlinlang.org/docs/reference/ • List of Kotlin resources https://kotlin.link/ _6 Useful links —
  6. 6. • Reference http://kotlinlang.org/docs/reference/ • List of Kotlin resources https://kotlin.link/ • Try Kotlin online http://try.kotl.in/ _7 Useful links —
  7. 7. • Reference http://kotlinlang.org/docs/reference/ • List of Kotlin resources https://kotlin.link/ • Try Kotlin online http://try.kotl.in/ • Slack https://kotlinlang.slack.com/ _8 Useful links —
  8. 8. • Problems Kotlin solves • Kotlin & Frameworks • Demo _9 What’s going on here —
  9. 9. • Problems Kotlin solves • Kotlin & Frameworks • Demo _10 What’s going on here —
  10. 10. // Java Map<Integer, Credentials> users = new HashMap<>(); users.put(1, new Credentials("vasya", "123456")); users.put(2, new Credentials("johny", "qwerty")); users.put(3, new Credentials("admin", "admin")); List<Integer> responseCodes = new ArrayList<>(); responseCodes.add(200); responseCodes.add(302); _11 1. Problem: Collections in Java —
  11. 11. // Java List<String> classpath = new ArrayList<>(); classpath.add(getBundleJarPath()); classpath.addAll(getPluginsPath()); _12 1. allure-framework/allure1 https://git.io/v9RQZ —
  12. 12. // Java List<String> classpath = new ArrayList<>(); classpath.add(getBundleJarPath()); classpath.addAll(getPluginsPath()); // Kotlin val classpath = listOf(getBundleJarPath(), getPluginsPath()) _13 1. allure-framework/allure1 https://git.io/v9RQZ —
  13. 13. // Java List<String> classpath = new ArrayList<>(); classpath.add(getBundleJarPath()); classpath.addAll(getPluginsPath()); // Kotlin val classpath = listOf(getBundleJarPath(), getPluginsPath()) List<String> _14 1. allure-framework/allure1 https://git.io/v9RQZ —
  14. 14. // Java List<String> classpath = new ArrayList<>(); classpath.add(getBundleJarPath()); classpath.addAll(getPluginsPath()); // Kotlin val classpath = listOf(getBundleJarPath(), getPluginsPath()) List<String> classpath.add("/usr/bin") _15 1. allure-framework/allure1 https://git.io/v9RQZ —
  15. 15. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() _16 1. reportportal/service-api https://git.io/v9RQy —
  16. 16. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{ }}; _17 1. reportportal/service-api https://git.io/v9RQy —
  17. 17. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{ put(AUTOMATION_BUG, Lists.newArrayList( new StatisticSubType(AUTOMATION_BUG.getLocator(), AUTOMATION_BUG.getValue(), "Automation Bug", "AB", "#f5d752"))); }}; _18 1. reportportal/service-api https://git.io/v9RQy —
  18. 18. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{ put(AUTOMATION_BUG, Lists.newArrayList( new StatisticSubType(AUTOMATION_BUG.getLocator(), AUTOMATION_BUG.getValue(), "Automation Bug", "AB", "#f5d752"))); ... put(TO_INVESTIGATE, Lists.newArrayList( new StatisticSubType(TO_INVESTIGATE.getLocator(), TO_INVESTIGATE.getValue(), "To Investigate", "TI", "#ffa500"))); }}; _19 1. reportportal/service-api https://git.io/v9RQy —
  19. 19. // Java val types = mapOf( AUTOMATION_BUG to listOf(StatisticSubType(AUTOMATION_BUG.locator, AUTOMATION_BUG.value, "Automation Bug", "AB", "#f5d752")), ... TO_INVESTIGATE to listOf(StatisticSubType(TO_INVESTIGATE.locator, TO_INVESTIGATE.value, "To Investigate", "TI", "#ffa500"))) _20 1. reportportal/service-api https://git.io/v9RQy —
  20. 20. _21 1. Collections: Traversing a map — // Java for (Map.Entry<Integer, Credentials> pair : users.entrySet()) { System.out.println(pair.getKey() + "->" + pair.getValue()); } // Kotlin users.forEach { k, v -> println("$k->$v") }
  21. 21. _22 1. allure-framework/allure1 https://git.io/v9R7E — // Java List<String> names = new ArrayList<>(); for (File file : files) { TestSuiteResult result = JAXB.unmarshal(file, TestSuiteResult.class); names.add(result.getName()); }
  22. 22. _23 1. allure-framework/allure1 https://git.io/v9R7E — // Java List<String> names = new ArrayList<>(); for (File file : files) { TestSuiteResult result = JAXB.unmarshal(file, TestSuiteResult.class); names.add(result.getName()); } // Kotlin val names = files.map { JAXB.unmarshal(it, TestSuiteResult::class.java).name }
  23. 23. • Java 7 • Java 8 (Stream API) • Groovy _24 1. Collections: Java & Groovy —
  24. 24. _25 2. Problem: framework can’t do what you need it to do — // Java public static void waitForElement(HtmlElement element, long timeout) { ... } waitForElement(link, 10); link.click();
  25. 25. _26
  26. 26. _27 2. Problem: framework can’t do what you need it to do —
  27. 27. _28 2. Solution: Extension functions — // Kotlin fun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T { ... return this } link.waitForIt().click() loginForm.waitForIt(10).guestButton.waitForIt().click()
  28. 28. _29 2. Solution: Extension functions — // Kotlin fun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T { ... return this } link.waitForIt().click() loginForm.waitForIt(10).guestButton.waitForIt().click()
  29. 29. _30 2. Solution: Extension functions — // Kotlin fun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T { ... return this } link.waitForIt().click() loginForm.waitForIt(10).guestButton.waitForIt().click()
  30. 30. _31 2. Solution: Extension functions — // Kotlin fun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T { ... return this } link.waitForIt().click() loginForm.waitForIt(10).guestButton.waitForIt().click()
  31. 31. _32 2. Solution: Extension functions —
  32. 32. • Java (Lombok https://projectlombok.org/) • Groovy (Extension Modules) _33 2. Extension functions: Java & Groovy —
  33. 33. // Java public class Credentials { private final String username; private final String password; } _34 3. Problem: small classes are not small —
  34. 34. // Java public class Credentials { private final String username; private final String password; public Credentials(String username, String password) { this.username = username; this.password = password; } } _35 3. Problem: small classes are not small —
  35. 35. // Java public class Credentials { private final String username; private final String password; public Credentials(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } } _36 3. Problem: small classes are not small —
  36. 36. // Java public class Credentials { private final String username; private final String password; public Credentials(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } @Override public String toString() { return username + '/' + password; } } _37 3. Problem: small classes are not small —
  37. 37. // Java public class Credentials { private final String username; private final String password; public Credentials(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } @Override public String toString() { return username + '/' + password; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Credentials that = (Credentials) o; if (username != null ? !username.equals(that.username) : that.username != null) return false; return password != null ? password.equals(that.password) : that.password == null; } @Override public int hashCode() { int result = username != null ? username.hashCode() : 0; return 31 * result + (password != null ? password.hashCode() : 0); } } _38 3. Problem: small classes are not small —
  38. 38. // Java public class Credentials { private final String username; private final String password; public Credentials(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public String getPassword() { return password; } @Override public String toString() { return username + '/' + password; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Credentials that = (Credentials) o; if (username != null ? !username.equals(that.username) : that.username != null) return false; return password != null ? password.equals(that.password) : that.password == null; } @Override public int hashCode() { int result = username != null ? username.hashCode() : 0; return 31 * result + (password != null ? password.hashCode() : 0); } } _39 3. Problem: 27 lines —
  39. 39. _40 3. griddynamics/jagger https://git.io/v9035 —
  40. 40. _41 3. Solution: Kotlin data classes — // Kotlin data class Credentials(val username: String, val password: String) val creds = Credentials("a", "b") println(creds.username) // a creds.username = "you can't do that" println(creds) // Credentials(username=a, password=b) println(creds == Credentials("a", "b")) // true
  41. 41. • Java (Lombok https://projectlombok.org/) • Groovy (@groovy.transform.Canonical) _42 3. Data classes: Java & Groovy —
  42. 42. // Java driver.findElement("button").click(); _43 4. Problem: steps in Allure report —
  43. 43. // Java @Step("Click the button") public void clickButton() { driver.findElement("button").click(); } clickButton(); _44 4. Problem: steps in Allure report —
  44. 44. // Java 8 @Step("{0}") public void step(String title, Runnable code) { code.run(); } step("Click the button", () -> { driver.findElement("button").click(); }); _45 4. Solution: steps in Allure report —
  45. 45. // Kotlin @Step("{0}") fun step(title: String, code: () -> Any) = code() step("Click the button") { driver.findElement("button").click() } _46 4. Solution: steps in Allure report —
  46. 46. // Java 8 step("Click the button", () -> { // your code here }); // Kotlin step("Click the button") { // your code here } _47 4. Solution: just compare —
  47. 47. • Java 7 • Java 8 • Groovy _48 4. Steps in Allure report: Java & Groovy —
  48. 48. // Java URLDecoder.decode(param, "UTF-8"); _49 5. Problem: checked exceptions —
  49. 49. // Java: String to md5 try { ... } catch (NoSuchAlgorithmException ex) { throw new RuntimeException( "Unable apply MD5 algorithm for password hashing: ", ex); } catch (UnsupportedEncodingException unsEx) { throw new RuntimeException( "Unable apply UTF-8 encoding for password string: ", unsEx); } _50 5. reportportal/service-api https://git.io/v9RF5 —
  50. 50. // Java try { screenshotUrl = new URL(screenshotUrl).toExternalForm(); } catch (MalformedURLException ignore) { } _51 5. codeborne/selenide https://git.io/v9Rbe —
  51. 51. // Java try { screenshotUrl = new URL(screenshotUrl).toExternalForm(); } catch (MalformedURLException ignore) { } // Kotlin screenshotUrl = URL(screenshotUrl).toExternalForm(); _52 5. Solution: no checked exceptions —
  52. 52. • Java • Groovy _53 5. Checked exceptions: Java & Groovy —
  53. 53. // Java Object errors = executeJavaScript("return window._selenide_jsErrors"); if (errors instanceof List) { return errorsFromList((List<Object>) errors); } _54 6. Problem: unchecked cast https://git.io/v9Ek1 —
  54. 54. // Java Object errors = executeJavaScript("return window._selenide_jsErrors"); if (errors instanceof List) { return errorsFromList((List<Object>) errors); } _55 6. Problem: unchecked cast https://git.io/v9Ek1 —
  55. 55. // Java Object errors = executeJavaScript("return window._selenide_jsErrors"); if (errors instanceof List) { return errorsFromList(((List<?>) errors) .stream() .filter(Object.class::isInstance) .map(Object.class::cast) .collect(toList()) ); } _56 6. Problem: unchecked cast https://git.io/v9Ek1 —
  56. 56. // Kotlin val errors = executeJavaScript("return window._selenide_jsErrors"); if (errors is List<*>) { return errorsFromList(errors.filterIsInstance<Object>()) } _57 6. Solution: Kotlin smart cast —
  57. 57. // Kotlin val errors = executeJavaScript("return window._selenide_jsErrors"); if (errors is List<*>) { return errorsFromList(errors.filterIsInstance<Object>()) } _58 6. Solution: Kotlin smart cast —
  58. 58. • Java • Groovy _59 6. Unchecked cast: Java & Groovy —
  59. 59. _60 7. Problem: — Java is verbose
  60. 60. // Java actualText.equals(expectedText) // Kotlin actualText == expectedText _61 7. Solution: Kotlin syntactic sugar —
  61. 61. // Java String s = "date: " + date + " and author: " + USER.getName(); String s = format("date: %s and author: %s", date, USER.getName()); // Kotlin val s = "date: $date and author: ${USER.name}" _62 7. Solution: String templates —
  62. 62. // Java for (int i = 0; i <= 10; i += 2) // Kotlin for (i in 0..10 step 2) _63 7. Solution: ranges —
  63. 63. // Java !list.isEmpty() // Kotlin list.isNotEmpty() _64 7. Solution: Kotlin rich standard library —
  64. 64. • Java • Groovy _65 7. Verbosity: Java & Groovy —
  65. 65. 8. Problem: —
  66. 66. var username: String username = null // compilation error _67 8. Solution: Null safety —
  67. 67. var username: String username = null // compilation error var username: String? username = null // ok _68 8. Null safety —
  68. 68. var username: String // non-nullable String username = null var username: String? // nullable String username = null _69 8. Null safety —
  69. 69. var username: String? = "Vasya" var count = username.length // compilation error _70 8. Null safety: safe call —
  70. 70. var username: String? = "Vasya" var count = username?.length // ok, count is 5 username = null var count = username?.length // ok, count is null _71 8. Null safety: safe call —
  71. 71. // Java username != null ? username : "Guest" _72 8. Null safety: Elvis operator —
  72. 72. // Java username != null ? username : "Guest" // Kotlin username ?: "Guest" var images = findAllImagesOnPage() ?: throw AssertionError("No images!") _73 8. Null safety: Elvis operator — ?:
  73. 73. • Null safety: Java (Optional<T> in Java8), Groovy • Safe call: Java, Groovy • Elvis operator: Java, Groovy _74 8. Null safety: Java & Groovy —
  74. 74. _75 1 2 3 4 5 6 7 8 Java 7 − − − − − − − − Java 8 ± − − + − − − − Groovy + ± + + + − + − Kotlin + + + + + + + + Java vs Groovy vs Kotlin —
  75. 75. • Not statically typed: runtime bugs • Not statically typed: performance • Not statically typed: IDE support • No null safety _76 Why not Groovy? —
  76. 76. • Problems Kotlin solves • Kotlin & Frameworks • Demo _77 What’s going on here —
  77. 77. Kotlin ~ Java — // Kotlin var counter: Int = 0 // Java private int counter = 0; public final int getCounter() { return counter; } public final void setCounter(int newCounter) { counter = newCounter; } _78
  78. 78. 1. JUnit 4 — @Before fun `start browser`() { ... } @Test fun `test name`() { ... } @Ignore("ISSUE-9000") @Test fun `ignored test`() { ... } @After fun `quit browser`() { ... } _79
  79. 79. _80
  80. 80. 1. JUnit 4: @Rule — _81 // Kotlin @Rule val tempFolder = TemporaryFolder()
  81. 81. 1. JUnit 4: @Rule — _82 // Kotlin @Rule val tempFolder = TemporaryFolder() org.junit.internal.runners.rules.ValidationError: The @Rule 'tempFolder' must be public.
  82. 82. 1. JUnit 4: @Rule — _83 // Kotlin @Rule val tempFolder = TemporaryFolder() // Java @Rule private final TemporaryFolder tempFolder = new TemporaryFolder(); public final TemporaryFolder getTempFolder() { return tempFolder; }
  83. 83. 1. JUnit 4: @Rule solution #1 — _84 // Kotlin @JvmField @Rule val tempFolder = TemporaryFolder() // Java @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
  84. 84. 1. JUnit 4: @Rule solution #2 — _85 // Kotlin @get:Rule val tempFolder = TemporaryFolder() // Java private final TemporaryFolder tempFolder = new TemporaryFolder(); @Rule public final TemporaryFolder getTempFolder() { return tempFolder; }
  85. 85. 1. JUnit 4: @Parameters — _86 // Kotlin @RunWith(Parameterized::class) class ParameterizedTest { @Parameters(name = "{0}") fun data(): Collection<Array<String>> = asList( arrayOf("firefox User-Agent"), arrayOf("chrome User-Agent") ) @Parameter lateinit var userAgent: String }
  86. 86. 1. JUnit 4: @Parameters — _87 // Kotlin @RunWith(Parameterized::class) class ParameterizedTest { @Parameters(name = "{0}") fun data(): Collection<Array<String>> = asList( arrayOf("firefox User-Agent"), arrayOf("chrome User-Agent") ) @Parameter lateinit var userAgent: String } java.lang.Exception: No public static parameters method on class com.jetbrains.ParameterizedTest
  87. 87. Companion object — // Kotlin class MyClass { companion object { fun looksLikeStatic() { ... } } } MyClass.looksLikeStatic() _88
  88. 88. 1. JUnit 4: @Parameters — _89 // Kotlin @RunWith(Parameterized::class) class ParameterizedTest { companion object { @Parameters(name = "{0}") fun data(): Collection<Array<String>> = asList( arrayOf("firefox User-Agent"), arrayOf("chrome User-Agent") ) } @Parameter lateinit var userAgent: String }
  89. 89. 1. JUnit 4: @Parameters — _90 // Kotlin @RunWith(Parameterized::class) class ParameterizedTest { companion object { @Parameters(name = "{0}") fun data(): Collection<Array<String>> = asList( arrayOf("firefox User-Agent"), arrayOf("chrome User-Agent") ) } @Parameter lateinit var userAgent: String } java.lang.Exception: No public static parameters method on class com.jetbrains.ParameterizedTest
  90. 90. 1. JUnit 4: @Parameters solution — _91 // Kotlin @RunWith(Parameterized::class) class ParameterizedTest { companion object { @Parameters(name = "{0}") @JvmStatic fun data(): Collection<Array<String>> = asList( arrayOf("firefox User-Agent"), arrayOf("chrome User-Agent") ) } @Parameter lateinit var userAgent: String }
  91. 91. 1. JUnit 4: @Parameters solution — _92 // Java @RunWith(Parameterized.class) class ParameterizedTest { public final static class Companion { public Collection<String[]> data() { return asList(...); } } public final static Companion Companion = new Companion(); @Parameters(name = "{0}") public final static Collection<String[]> data() { return Companion.data(); } }
  92. 92. 1. JUnit 4: @Parameters solution — _93 // Java @RunWith(Parameterized.class) class ParameterizedTest { public final static class Companion { public Collection<String[]> data() { return asList(...); } } public final static Companion Companion = new Companion(); @Parameters(name = "{0}") public final static Collection<String[]> data() { return Companion.data(); } }
  93. 93. 1. JUnit 4: @Parameters solution — _94 // Java @RunWith(Parameterized.class) class ParameterizedTest { public final static class Companion { public Collection<String[]> data() { return asList(...); } } public final static Companion Companion = new Companion(); @Parameters(name = "{0}") public final static Collection<String[]> data() { return Companion.data(); } }
  94. 94. 1. JUnit 4: @Parameters solution — _95 // Java @RunWith(Parameterized.class) class ParameterizedTest { public final static class Companion { public Collection<String[]> data() { return asList(...); } } public final static Companion Companion = new Companion(); @Parameters(name = "{0}") public final static Collection<String[]> data() { return Companion.data(); } }
  95. 95. 2. HtmlElements 1.*: element — _96 @FindBy(css = "form[name='LoginForm']") class LoginForm : HtmlElement() { @FindBy(css = "#username") lateinit var usernameInput: HtmlElement @FindBy(css = "#password") lateinit var passwordInput: HtmlElement @FindBy(xpath = ".//button") lateinit var loginButton: HtmlElement fun login(username: String, password: String) { usernameInput.sendKeys(username) passwordInput.sendKeys(password) loginButton.click() } }
  96. 96. 2. HtmlElements 1.*: page object — _97 class Page(val driver: WebDriver) { init { PageFactory.initElements( HtmlElementDecorator( HtmlElementLocatorFactory(driver)), this) } lateinit var loginForm: LoginForm @FindBy(css = ".dashboard-buttons_add") lateinit var addWidgetButton: HtmlElement }
  97. 97. _98
  98. 98. 2. HtmlElements 1.*: collection of elements — _99 // Kotlin @FindBy(css = "a") lateinit var links: List<HtmlElement> ... links.forEach { ... }
  99. 99. 2. HtmlElements 1.*: collection of elements — _100 // Kotlin @FindBy(css = "a") lateinit var links: List<HtmlElement> ... links.forEach { ... } kotlin.UninitializedPropertyAccessException
  100. 100. 2. HtmlElements 1.*: collection of elements — _101 // Kotlin @FindBy(css = "a") lateinit var links: List<HtmlElement> java.util.List<? extends ru.yandex.qatools.htmlelements.element.HtmlElement>
  101. 101. 2. HtmlElements 1.*: collection of elements — _102 // Kotlin @FindBy(css = "a") lateinit var links: List<@JvmSuppressWildcards HtmlElement> java.util.List<ru.yandex.qatools.htmlelements.element.HtmlElement> • https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#variant-generics
  102. 102. 3. Allure — _103 // Kotlin @Title("This is title") @Description("This is description") @Test fun test() { ... } @Step("Change Home URL to {0}") fun changeHomeURL(homeURL: String) { ... } @Attachment(value = "{0}", type = "text/plain") fun attachText(name: String = "text", text: String?) = text
  103. 103. _104
  104. 104. 3. Allure: org.aspectj:aspectjweaver:1.8.10 — _105 // Kotlin links.forEach { println(it.text) }
  105. 105. 3. Allure: org.aspectj:aspectjweaver:1.8.10 — _106 // Kotlin links.forEach { println(it.text) } java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable
  106. 106. 3. Allure: org.aspectj:aspectjweaver:1.8.10 — _107 // Kotlin links.forEach { println(it.text) } java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable • https://bugs.eclipse.org/bugs/show_bug.cgi?id=500796
  107. 107. 4. Selenide: $ is reserved — _108 // Kotlin import com.codeborne.selenide.Selenide.$ Error: Kotlin: Qualified name must be a '.'-separated identifier list
  108. 108. 4. Selenide: $ is reserved — _109 // Kotlin import com.codeborne.selenide.Selenide.`$` import com.codeborne.selenide.Selenide.`$$` ... `$`("a") `$$`("a")
  109. 109. _110
  110. 110. 5. Selenium WebDriver — _111 // Kotlin val driver = RemoteWebDriver( URL("http://grid.company.com:4444/wd/hub"), DesiredCapabilities.chrome()) driver.manage().window().size = Dimension(800, 600) driver.get("https://heisenbug-piter.ru") val link = driver.findElement(By.cssSelector("a.navbar- brand")) val text = link.text val clazz = link.getAttribute("class") Actions(driver).moveToElement(link).perform() (driver as TakesScreenshot).getScreenshotAs(OutputType.BYTES) driver.quit()
  111. 111. _112
  112. 112. 6. REST Assured — _113 // Kotlin fun createIssue(issue: Issue): Issue = given() .baseUri("http://host:8080") .header("Authorization", "*token*") .contentType("application/json") .accept("application/json") .queryParams(mapOf("fields" to "id,project(id,shortName),numberInProject")) .with().body(issue) .post("/api/issues") .`as`(Issue::class.java)
  113. 113. • Problems Kotlin solves • Kotlin & Frameworks • Demo _114 What’s going on here —
  114. 114. _115 Example web tests project in Kotlin — gradle-docker-plugin
  115. 115. _116 Example web tests project in Kotlin — gradle-docker-plugin JUnit, Html Elements
  116. 116. _117 Example web tests project in Kotlin — gradle-docker-plugin JUnit, Html Elements
  117. 117. _118 • Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } = OK https://github.com/leonsabr/web-tests-in-kotlin-demo —
  118. 118. _119 • Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } • Java interoperability https://github.com/leonsabr/web-tests-in-kotlin-demo —
  119. 119. _120 https://github.com/leonsabr/web-tests-in-kotlin-demo —
  120. 120. _121 java kotlin ∆ main 675 434 35,7% test 89 84 5,6% total 764 518 32,2% • Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } • Java interoperability • Conciseness (lines of code) https://github.com/leonsabr/web-tests-in-kotlin-demo —
  121. 121. Thank you for your attention — jetbrains.com leonid.rudenko@jetbrains.com @leonsabr
  • SergeyZastavnyuk

    Apr. 30, 2020
  • SergeiKramer1

    Jul. 22, 2017
  • asolntsev

    Jul. 10, 2017

Heisenbug St. Petersburg, 04.06.2017

Views

Total views

4,051

On Slideshare

0

From embeds

0

Number of embeds

2,201

Actions

Downloads

28

Shares

0

Comments

0

Likes

3

×