SlideShare a Scribd company logo
1 of 122
Download to read offline
Java → Kotlin:
Tests Made
Simple
—
Leonid Rudenko
_2
Why?
—
_3
Why?
—
• Reference http://kotlinlang.org/docs/reference/
_5
Useful links
—
• Reference http://kotlinlang.org/docs/reference/
• List of Kotlin resources https://kotlin.link/
_6
Useful links
—
• Reference http://kotlinlang.org/docs/reference/
• List of Kotlin resources https://kotlin.link/
• Try Kotlin online http://try.kotl.in/
_7
Useful links
—
• 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
—
• Problems Kotlin solves
• Kotlin & Frameworks
• Demo
_9
What’s going on here
—
• Problems Kotlin solves
• Kotlin & Frameworks
• Demo
_10
What’s going on here
—
// 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
—
// Java
List<String> classpath = new ArrayList<>();
classpath.add(getBundleJarPath());
classpath.addAll(getPluginsPath());
_12
1. allure-framework/allure1 https://git.io/v9RQZ
—
// 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
—
// 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
—
// 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
—
// Java
Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>()
_16
1. reportportal/service-api https://git.io/v9RQy
—
// Java
Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>()
{{
}};
_17
1. reportportal/service-api https://git.io/v9RQy
—
// 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
—
// 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
—
// 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
—
_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")
}
_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());
}
_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
}
• Java 7
• Java 8 (Stream API)
• Groovy
_24
1. Collections: Java & Groovy
—
_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();
_26
_27
2. Problem: framework can’t do what you need it to do
—
_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()
_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()
_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()
_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()
_32
2. Solution: Extension functions
—
• Java (Lombok https://projectlombok.org/)
• Groovy (Extension Modules)
_33
2. Extension functions: Java & Groovy
—
// Java
public class Credentials {
private final String username;
private final String password;
}
_34
3. Problem: small classes are not small
—
// 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
—
// 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
—
// 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
—
// 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
—
// 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
—
_40
3. griddynamics/jagger https://git.io/v9035
—
_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
• Java (Lombok https://projectlombok.org/)
• Groovy (@groovy.transform.Canonical)
_42
3. Data classes: Java & Groovy
—
// Java
driver.findElement("button").click();
_43
4. Problem: steps in Allure report
—
// Java
@Step("Click the button")
public void clickButton() {
driver.findElement("button").click();
}
clickButton();
_44
4. Problem: steps in Allure report
—
// 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
—
// 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
—
// Java 8
step("Click the button", () -> {
// your code here
});
// Kotlin
step("Click the button") {
// your code here
}
_47
4. Solution: just compare
—
• Java 7
• Java 8
• Groovy
_48
4. Steps in Allure report: Java & Groovy
—
// Java
URLDecoder.decode(param, "UTF-8");
_49
5. Problem: checked exceptions
—
// 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
—
// Java
try {
screenshotUrl = new URL(screenshotUrl).toExternalForm();
} catch (MalformedURLException ignore) { }
_51
5. codeborne/selenide https://git.io/v9Rbe
—
// Java
try {
screenshotUrl = new URL(screenshotUrl).toExternalForm();
} catch (MalformedURLException ignore) { }
// Kotlin
screenshotUrl = URL(screenshotUrl).toExternalForm();
_52
5. Solution: no checked exceptions
—
• Java
• Groovy
_53
5. Checked exceptions: Java & Groovy
—
// 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
—
// 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
—
// 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
—
// Kotlin
val errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors is List<*>) {
return errorsFromList(errors.filterIsInstance<Object>())
}
_57
6. Solution: Kotlin smart cast
—
// Kotlin
val errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors is List<*>) {
return errorsFromList(errors.filterIsInstance<Object>())
}
_58
6. Solution: Kotlin smart cast
—
• Java
• Groovy
_59
6. Unchecked cast: Java & Groovy
—
_60
7. Problem:
— Java is verbose
// Java
actualText.equals(expectedText)
// Kotlin
actualText == expectedText
_61
7. Solution: Kotlin syntactic sugar
—
// 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
—
// Java
for (int i = 0; i <= 10; i += 2)
// Kotlin
for (i in 0..10 step 2)
_63
7. Solution: ranges
—
// Java
!list.isEmpty()
// Kotlin
list.isNotEmpty()
_64
7. Solution: Kotlin rich standard library
—
• Java
• Groovy
_65
7. Verbosity: Java & Groovy
—
8. Problem:
—
var username: String
username = null // compilation error
_67
8. Solution: Null safety
—
var username: String
username = null // compilation error
var username: String?
username = null // ok
_68
8. Null safety
—
var username: String // non-nullable String
username = null
var username: String? // nullable String
username = null
_69
8. Null safety
—
var username: String? = "Vasya"
var count = username.length // compilation error
_70
8. Null safety: safe call
—
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
—
// Java
username != null ? username : "Guest"
_72
8. Null safety: Elvis operator
—
// Java
username != null ? username : "Guest"
// Kotlin
username ?: "Guest"
var images = findAllImagesOnPage()
?: throw AssertionError("No images!")
_73
8. Null safety: Elvis operator
—
?:
• Null safety: Java (Optional<T> in Java8), Groovy
• Safe call: Java, Groovy
• Elvis operator: Java, Groovy
_74
8. Null safety: Java & Groovy
—
_75
1 2 3 4 5 6 7 8
Java 7 − − − − − − − −
Java 8 ± − − + − − − −
Groovy + ± + + + − + −
Kotlin + + + + + + + +
Java vs Groovy vs Kotlin
—
• Not statically typed: runtime bugs
• Not statically typed: performance
• Not statically typed: IDE support
• No null safety
_76
Why not Groovy?
—
• Problems Kotlin solves
• Kotlin & Frameworks
• Demo
_77
What’s going on here
—
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
1. JUnit 4
—
@Before fun `start browser`() { ... }
@Test fun `test name`() { ... }
@Ignore("ISSUE-9000")
@Test fun `ignored test`() { ... }
@After fun `quit browser`() { ... }
_79
_80
1. JUnit 4: @Rule
—
_81
// Kotlin
@Rule val tempFolder = TemporaryFolder()
1. JUnit 4: @Rule
—
_82
// Kotlin
@Rule val tempFolder = TemporaryFolder()
org.junit.internal.runners.rules.ValidationError: The @Rule
'tempFolder' must be public.
1. JUnit 4: @Rule
—
_83
// Kotlin
@Rule val tempFolder = TemporaryFolder()
// Java
@Rule
private final TemporaryFolder tempFolder = new TemporaryFolder();
public final TemporaryFolder getTempFolder() {
return tempFolder;
}
1. JUnit 4: @Rule solution #1
—
_84
// Kotlin
@JvmField @Rule val tempFolder = TemporaryFolder()
// Java
@Rule
public final TemporaryFolder tempFolder = new TemporaryFolder();
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;
}
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
}
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
Companion object
—
// Kotlin
class MyClass {
companion object {
fun looksLikeStatic() { ... }
}
}
MyClass.looksLikeStatic()
_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
}
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
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
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
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()
}
}
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
}
_98
2. HtmlElements 1.*: collection of elements
—
_99
// Kotlin
@FindBy(css = "a")
lateinit var links: List<HtmlElement>
...
links.forEach { ... }
2. HtmlElements 1.*: collection of elements
—
_100
// Kotlin
@FindBy(css = "a")
lateinit var links: List<HtmlElement>
...
links.forEach { ... }
kotlin.UninitializedPropertyAccessException
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>
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
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
_104
3. Allure: org.aspectj:aspectjweaver:1.8.10
—
_105
// Kotlin
links.forEach {
println(it.text)
}
3. Allure: org.aspectj:aspectjweaver:1.8.10
—
_106
// Kotlin
links.forEach {
println(it.text)
}
java.lang.ClassFormatError: Invalid index 6 in
LocalVariableTable
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
4. Selenide: $ is reserved
—
_108
// Kotlin
import com.codeborne.selenide.Selenide.$
Error: Kotlin: Qualified name must be a '.'-separated
identifier list
4. Selenide: $ is reserved
—
_109
// Kotlin
import com.codeborne.selenide.Selenide.`$`
import com.codeborne.selenide.Selenide.`$$`
...
`$`("a")
`$$`("a")
_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()
_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)
• Problems Kotlin solves
• Kotlin & Frameworks
• Demo
_114
What’s going on here
—
_115
Example web tests project in Kotlin
—
gradle-docker-plugin
_116
Example web tests project in Kotlin
—
gradle-docker-plugin
JUnit,	Html	Elements
_117
Example web tests project in Kotlin
—
gradle-docker-plugin
JUnit,	Html	Elements
_118
• Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } = OK
https://github.com/leonsabr/web-tests-in-kotlin-demo
—
_119
• Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure }
• Java interoperability
https://github.com/leonsabr/web-tests-in-kotlin-demo
—
_120
https://github.com/leonsabr/web-tests-in-kotlin-demo
—
_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
—
Thank you
for your attention
—
jetbrains.com
leonid.rudenko@jetbrains.com
@leonsabr

More Related Content

What's hot

OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?Henri Tremblay
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with ClojureJohn Stevenson
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Introthnetos
 
Kotlin: Why Do You Care?
Kotlin: Why Do You Care?Kotlin: Why Do You Care?
Kotlin: Why Do You Care?intelliyole
 
Clojure, Plain and Simple
Clojure, Plain and SimpleClojure, Plain and Simple
Clojure, Plain and SimpleBen Mabey
 
Advanced Java Practical File
Advanced Java Practical FileAdvanced Java Practical File
Advanced Java Practical FileSoumya Behera
 
Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Sunghyouk Bae
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - StockholmJan Kronquist
 
Java7 New Features and Code Examples
Java7 New Features and Code ExamplesJava7 New Features and Code Examples
Java7 New Features and Code ExamplesNaresh Chintalcheru
 
Lombokの紹介
Lombokの紹介Lombokの紹介
Lombokの紹介onozaty
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring frameworkSunghyouk Bae
 
Refactor legacy code through pure functions
Refactor legacy code through pure functionsRefactor legacy code through pure functions
Refactor legacy code through pure functionsAlexandru Bolboaca
 
02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LABHari Christian
 
Advance Java Programs skeleton
Advance Java Programs skeletonAdvance Java Programs skeleton
Advance Java Programs skeletonIram Ramrajkar
 
Dear Kotliners - Java Developers are Humans too
Dear Kotliners - Java Developers are Humans tooDear Kotliners - Java Developers are Humans too
Dear Kotliners - Java Developers are Humans tooVivek Chanddru
 

What's hot (20)

OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
OracleCode One 2018: Java 5, 6, 7, 8, 9, 10, 11: What Did You Miss?
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with Clojure
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Intro
 
Kotlin: Why Do You Care?
Kotlin: Why Do You Care?Kotlin: Why Do You Care?
Kotlin: Why Do You Care?
 
Clojure, Plain and Simple
Clojure, Plain and SimpleClojure, Plain and Simple
Clojure, Plain and Simple
 
Sam wd programs
Sam wd programsSam wd programs
Sam wd programs
 
Advanced Java Practical File
Advanced Java Practical FileAdvanced Java Practical File
Advanced Java Practical File
 
Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
 
Java7 New Features and Code Examples
Java7 New Features and Code ExamplesJava7 New Features and Code Examples
Java7 New Features and Code Examples
 
Lombokの紹介
Lombokの紹介Lombokの紹介
Lombokの紹介
 
JDK1.6
JDK1.6JDK1.6
JDK1.6
 
Ad java prac sol set
Ad java prac sol setAd java prac sol set
Ad java prac sol set
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring framework
 
Kotlin intro
Kotlin introKotlin intro
Kotlin intro
 
Refactor legacy code through pure functions
Refactor legacy code through pure functionsRefactor legacy code through pure functions
Refactor legacy code through pure functions
 
02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB
 
Advance Java Programs skeleton
Advance Java Programs skeletonAdvance Java Programs skeleton
Advance Java Programs skeleton
 
Kotlin - Better Java
Kotlin - Better JavaKotlin - Better Java
Kotlin - Better Java
 
Dear Kotliners - Java Developers are Humans too
Dear Kotliners - Java Developers are Humans tooDear Kotliners - Java Developers are Humans too
Dear Kotliners - Java Developers are Humans too
 

Similar to Java → kotlin: Tests Made Simple

Use of Apache Commons and Utilities
Use of Apache Commons and UtilitiesUse of Apache Commons and Utilities
Use of Apache Commons and UtilitiesPramod Kumar
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Jesper Kamstrup Linnet
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages VictorSzoltysek
 
Nice to meet Kotlin
Nice to meet KotlinNice to meet Kotlin
Nice to meet KotlinJieyi Wu
 
Building Single-Page Web Appplications in dart - Devoxx France 2013
Building Single-Page Web Appplications in dart - Devoxx France 2013Building Single-Page Web Appplications in dart - Devoxx France 2013
Building Single-Page Web Appplications in dart - Devoxx France 2013yohanbeschi
 
Esoft Metro Campus - Certificate in java basics
Esoft Metro Campus - Certificate in java basicsEsoft Metro Campus - Certificate in java basics
Esoft Metro Campus - Certificate in java basicsRasan Samarasinghe
 
Spock: Test Well and Prosper
Spock: Test Well and ProsperSpock: Test Well and Prosper
Spock: Test Well and ProsperKen Kousen
 
Introduction à Dart
Introduction à DartIntroduction à Dart
Introduction à DartSOAT
 
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem nova
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem novaKotlin+MicroProfile: Ensinando 20 anos para uma linguagem nova
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem novaVíctor Leonel Orozco López
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)quantumiq448
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaDILo Surabaya
 
Java oops PPT
Java oops PPTJava oops PPT
Java oops PPTkishu0005
 
02-OOP with Java.ppt
02-OOP with Java.ppt02-OOP with Java.ppt
02-OOP with Java.pptEmanAsem4
 

Similar to Java → kotlin: Tests Made Simple (20)

OOP Lab Report.docx
OOP Lab Report.docxOOP Lab Report.docx
OOP Lab Report.docx
 
Scala - en bedre Java?
Scala - en bedre Java?Scala - en bedre Java?
Scala - en bedre Java?
 
Use of Apache Commons and Utilities
Use of Apache Commons and UtilitiesUse of Apache Commons and Utilities
Use of Apache Commons and Utilities
 
Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?Scala - en bedre og mere effektiv Java?
Scala - en bedre og mere effektiv Java?
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages
 
Nice to meet Kotlin
Nice to meet KotlinNice to meet Kotlin
Nice to meet Kotlin
 
Building Single-Page Web Appplications in dart - Devoxx France 2013
Building Single-Page Web Appplications in dart - Devoxx France 2013Building Single-Page Web Appplications in dart - Devoxx France 2013
Building Single-Page Web Appplications in dart - Devoxx France 2013
 
Esoft Metro Campus - Certificate in java basics
Esoft Metro Campus - Certificate in java basicsEsoft Metro Campus - Certificate in java basics
Esoft Metro Campus - Certificate in java basics
 
Spock: Test Well and Prosper
Spock: Test Well and ProsperSpock: Test Well and Prosper
Spock: Test Well and Prosper
 
Introduction à Dart
Introduction à DartIntroduction à Dart
Introduction à Dart
 
Sequelize
SequelizeSequelize
Sequelize
 
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem nova
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem novaKotlin+MicroProfile: Ensinando 20 anos para uma linguagem nova
Kotlin+MicroProfile: Ensinando 20 anos para uma linguagem nova
 
Pattern Matching in Java 14
Pattern Matching in Java 14Pattern Matching in Java 14
Pattern Matching in Java 14
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Workshop Scala
Workshop ScalaWorkshop Scala
Workshop Scala
 
Object oriented concepts
Object oriented conceptsObject oriented concepts
Object oriented concepts
 
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)
Java PRESENTATION(PACKAGES,CLASSES,VARIABLES,FLOW CONTROL,EXCEPTION)
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo Surabaya
 
Java oops PPT
Java oops PPTJava oops PPT
Java oops PPT
 
02-OOP with Java.ppt
02-OOP with Java.ppt02-OOP with Java.ppt
02-OOP with Java.ppt
 

Recently uploaded

%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...masabamasaba
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...masabamasaba
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfayushiqss
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsJhone kinadey
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareJim McKeeth
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...Shane Coughlan
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech studentsHimanshiGarg82
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...masabamasaba
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024Mind IT Systems
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...panagenda
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburgmasabamasaba
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfonteinmasabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...masabamasaba
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionOnePlan Solutions
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationShrmpro
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park masabamasaba
 

Recently uploaded (20)

%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 

Java → kotlin: Tests Made Simple

  • 1. Java → Kotlin: Tests Made Simple — Leonid Rudenko
  • 4.
  • 6. • Reference http://kotlinlang.org/docs/reference/ • List of Kotlin resources https://kotlin.link/ _6 Useful links —
  • 7. • Reference http://kotlinlang.org/docs/reference/ • List of Kotlin resources https://kotlin.link/ • Try Kotlin online http://try.kotl.in/ _7 Useful links —
  • 8. • 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 —
  • 9. • Problems Kotlin solves • Kotlin & Frameworks • Demo _9 What’s going on here —
  • 10. • Problems Kotlin solves • Kotlin & Frameworks • Demo _10 What’s going on here —
  • 11. // 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 —
  • 12. // Java List<String> classpath = new ArrayList<>(); classpath.add(getBundleJarPath()); classpath.addAll(getPluginsPath()); _12 1. allure-framework/allure1 https://git.io/v9RQZ —
  • 13. // 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 —
  • 14. // 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 —
  • 15. // 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 —
  • 16. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() _16 1. reportportal/service-api https://git.io/v9RQy —
  • 17. // Java Map<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{ }}; _17 1. reportportal/service-api https://git.io/v9RQy —
  • 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"))); }}; _18 1. reportportal/service-api https://git.io/v9RQy —
  • 19. // 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 —
  • 20. // 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 —
  • 21. _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") }
  • 22. _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()); }
  • 23. _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 }
  • 24. • Java 7 • Java 8 (Stream API) • Groovy _24 1. Collections: Java & Groovy —
  • 25. _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();
  • 26. _26
  • 27. _27 2. Problem: framework can’t do what you need it to do —
  • 28. _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()
  • 29. _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()
  • 30. _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()
  • 31. _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()
  • 32. _32 2. Solution: Extension functions —
  • 33. • Java (Lombok https://projectlombok.org/) • Groovy (Extension Modules) _33 2. Extension functions: Java & Groovy —
  • 34. // Java public class Credentials { private final String username; private final String password; } _34 3. Problem: small classes are not small —
  • 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; } } _35 3. Problem: small classes are not small —
  • 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; } } _36 3. Problem: small classes are not small —
  • 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; } } _37 3. Problem: small classes are not small —
  • 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); } } _38 3. Problem: small classes are not small —
  • 39. // 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 —
  • 41. _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
  • 42. • Java (Lombok https://projectlombok.org/) • Groovy (@groovy.transform.Canonical) _42 3. Data classes: Java & Groovy —
  • 44. // Java @Step("Click the button") public void clickButton() { driver.findElement("button").click(); } clickButton(); _44 4. Problem: steps in Allure report —
  • 45. // 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 —
  • 46. // 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 —
  • 47. // Java 8 step("Click the button", () -> { // your code here }); // Kotlin step("Click the button") { // your code here } _47 4. Solution: just compare —
  • 48. • Java 7 • Java 8 • Groovy _48 4. Steps in Allure report: Java & Groovy —
  • 49. // Java URLDecoder.decode(param, "UTF-8"); _49 5. Problem: checked exceptions —
  • 50. // 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 —
  • 51. // Java try { screenshotUrl = new URL(screenshotUrl).toExternalForm(); } catch (MalformedURLException ignore) { } _51 5. codeborne/selenide https://git.io/v9Rbe —
  • 52. // Java try { screenshotUrl = new URL(screenshotUrl).toExternalForm(); } catch (MalformedURLException ignore) { } // Kotlin screenshotUrl = URL(screenshotUrl).toExternalForm(); _52 5. Solution: no checked exceptions —
  • 53. • Java • Groovy _53 5. Checked exceptions: Java & Groovy —
  • 54. // 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 —
  • 55. // 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 —
  • 56. // 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 —
  • 57. // Kotlin val errors = executeJavaScript("return window._selenide_jsErrors"); if (errors is List<*>) { return errorsFromList(errors.filterIsInstance<Object>()) } _57 6. Solution: Kotlin smart cast —
  • 58. // Kotlin val errors = executeJavaScript("return window._selenide_jsErrors"); if (errors is List<*>) { return errorsFromList(errors.filterIsInstance<Object>()) } _58 6. Solution: Kotlin smart cast —
  • 59. • Java • Groovy _59 6. Unchecked cast: Java & Groovy —
  • 61. // Java actualText.equals(expectedText) // Kotlin actualText == expectedText _61 7. Solution: Kotlin syntactic sugar —
  • 62. // 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 —
  • 63. // Java for (int i = 0; i <= 10; i += 2) // Kotlin for (i in 0..10 step 2) _63 7. Solution: ranges —
  • 64. // Java !list.isEmpty() // Kotlin list.isNotEmpty() _64 7. Solution: Kotlin rich standard library —
  • 65. • Java • Groovy _65 7. Verbosity: Java & Groovy —
  • 67. var username: String username = null // compilation error _67 8. Solution: Null safety —
  • 68. var username: String username = null // compilation error var username: String? username = null // ok _68 8. Null safety —
  • 69. var username: String // non-nullable String username = null var username: String? // nullable String username = null _69 8. Null safety —
  • 70. var username: String? = "Vasya" var count = username.length // compilation error _70 8. Null safety: safe call —
  • 71. 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 —
  • 72. // Java username != null ? username : "Guest" _72 8. Null safety: Elvis operator —
  • 73. // Java username != null ? username : "Guest" // Kotlin username ?: "Guest" var images = findAllImagesOnPage() ?: throw AssertionError("No images!") _73 8. Null safety: Elvis operator — ?:
  • 74. • Null safety: Java (Optional<T> in Java8), Groovy • Safe call: Java, Groovy • Elvis operator: Java, Groovy _74 8. Null safety: Java & Groovy —
  • 75. _75 1 2 3 4 5 6 7 8 Java 7 − − − − − − − − Java 8 ± − − + − − − − Groovy + ± + + + − + − Kotlin + + + + + + + + Java vs Groovy vs Kotlin —
  • 76. • Not statically typed: runtime bugs • Not statically typed: performance • Not statically typed: IDE support • No null safety _76 Why not Groovy? —
  • 77. • Problems Kotlin solves • Kotlin & Frameworks • Demo _77 What’s going on here —
  • 78. 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
  • 79. 1. JUnit 4 — @Before fun `start browser`() { ... } @Test fun `test name`() { ... } @Ignore("ISSUE-9000") @Test fun `ignored test`() { ... } @After fun `quit browser`() { ... } _79
  • 80. _80
  • 81. 1. JUnit 4: @Rule — _81 // Kotlin @Rule val tempFolder = TemporaryFolder()
  • 82. 1. JUnit 4: @Rule — _82 // Kotlin @Rule val tempFolder = TemporaryFolder() org.junit.internal.runners.rules.ValidationError: The @Rule 'tempFolder' must be public.
  • 83. 1. JUnit 4: @Rule — _83 // Kotlin @Rule val tempFolder = TemporaryFolder() // Java @Rule private final TemporaryFolder tempFolder = new TemporaryFolder(); public final TemporaryFolder getTempFolder() { return tempFolder; }
  • 84. 1. JUnit 4: @Rule solution #1 — _84 // Kotlin @JvmField @Rule val tempFolder = TemporaryFolder() // Java @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
  • 85. 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; }
  • 86. 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 }
  • 87. 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
  • 88. Companion object — // Kotlin class MyClass { companion object { fun looksLikeStatic() { ... } } } MyClass.looksLikeStatic() _88
  • 89. 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 }
  • 90. 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
  • 91. 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 }
  • 92. 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(); } }
  • 93. 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(); } }
  • 94. 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(); } }
  • 95. 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(); } }
  • 96. 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() } }
  • 97. 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 }
  • 98. _98
  • 99. 2. HtmlElements 1.*: collection of elements — _99 // Kotlin @FindBy(css = "a") lateinit var links: List<HtmlElement> ... links.forEach { ... }
  • 100. 2. HtmlElements 1.*: collection of elements — _100 // Kotlin @FindBy(css = "a") lateinit var links: List<HtmlElement> ... links.forEach { ... } kotlin.UninitializedPropertyAccessException
  • 101. 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>
  • 102. 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
  • 103. 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
  • 104. _104
  • 105. 3. Allure: org.aspectj:aspectjweaver:1.8.10 — _105 // Kotlin links.forEach { println(it.text) }
  • 106. 3. Allure: org.aspectj:aspectjweaver:1.8.10 — _106 // Kotlin links.forEach { println(it.text) } java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable
  • 107. 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
  • 108. 4. Selenide: $ is reserved — _108 // Kotlin import com.codeborne.selenide.Selenide.$ Error: Kotlin: Qualified name must be a '.'-separated identifier list
  • 109. 4. Selenide: $ is reserved — _109 // Kotlin import com.codeborne.selenide.Selenide.`$` import com.codeborne.selenide.Selenide.`$$` ... `$`("a") `$$`("a")
  • 110. _110
  • 111. 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()
  • 112. _112
  • 113. 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)
  • 114. • Problems Kotlin solves • Kotlin & Frameworks • Demo _114 What’s going on here —
  • 115. _115 Example web tests project in Kotlin — gradle-docker-plugin
  • 116. _116 Example web tests project in Kotlin — gradle-docker-plugin JUnit, Html Elements
  • 117. _117 Example web tests project in Kotlin — gradle-docker-plugin JUnit, Html Elements
  • 118. _118 • Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } = OK https://github.com/leonsabr/web-tests-in-kotlin-demo —
  • 119. _119 • Kotlin + { Gradle, JUnit, Selenium, Html Elements, Allure } • Java interoperability https://github.com/leonsabr/web-tests-in-kotlin-demo —
  • 121. _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 —
  • 122. Thank you for your attention — jetbrains.com leonid.rudenko@jetbrains.com @leonsabr