Upload
leonsabr
View
1.212
Download
0
Embed Size (px)
Citation preview
• Reference http://kotlinlang.org/docs/reference/
• List of Kotlin resources https://kotlin.link/
_6Useful links—
• Reference http://kotlinlang.org/docs/reference/
• List of Kotlin resources https://kotlin.link/
• Try Kotlin online http://try.kotl.in/
_7Useful 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/
_8Useful links—
// JavaMap<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);
_111. Problem: Collections in Java—
// JavaList<String> classpath = new ArrayList<>();classpath.add(getBundleJarPath());classpath.addAll(getPluginsPath());
_121. allure-framework/allure1 https://git.io/v9RQZ—
// JavaList<String> classpath = new ArrayList<>();classpath.add(getBundleJarPath());classpath.addAll(getPluginsPath());
// Kotlinval classpath = listOf(getBundleJarPath(), getPluginsPath())
_131. allure-framework/allure1 https://git.io/v9RQZ—
// JavaList<String> classpath = new ArrayList<>();classpath.add(getBundleJarPath());classpath.addAll(getPluginsPath());
// Kotlinval classpath = listOf(getBundleJarPath(), getPluginsPath())
List<String>
_141. allure-framework/allure1 https://git.io/v9RQZ—
// JavaList<String> classpath = new ArrayList<>();classpath.add(getBundleJarPath());classpath.addAll(getPluginsPath());
// Kotlinval classpath = listOf(getBundleJarPath(), getPluginsPath())
List<String>
classpath.add("/usr/bin")
_151. allure-framework/allure1 https://git.io/v9RQZ—
// JavaMap<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>()
_161. reportportal/service-api https://git.io/v9RQy—
// JavaMap<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{
}};
_171. reportportal/service-api https://git.io/v9RQy—
// JavaMap<TestItemIssueType, List<StatisticSubType>> types = new HashMap<>() {{
put(AUTOMATION_BUG, Lists.newArrayList(new StatisticSubType(AUTOMATION_BUG.getLocator(),
AUTOMATION_BUG.getValue(), "Automation Bug", "AB", "#f5d752")));
}};
_181. reportportal/service-api https://git.io/v9RQy—
// JavaMap<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")));}};
_191. reportportal/service-api https://git.io/v9RQy—
// Javaval 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")))
_201. reportportal/service-api https://git.io/v9RQy—
_211. Collections: Traversing a map—// Javafor (Map.Entry<Integer, Credentials> pair : users.entrySet()) {
System.out.println(pair.getKey() + "->" + pair.getValue());}
// Kotlinusers.forEach { k, v ->
println("$k->$v")}
_221. allure-framework/allure1 https://git.io/v9R7E—// JavaList<String> names = new ArrayList<>();for (File file : files) {
TestSuiteResult result= JAXB.unmarshal(file, TestSuiteResult.class);
names.add(result.getName());}
_231. allure-framework/allure1 https://git.io/v9R7E—// JavaList<String> names = new ArrayList<>();for (File file : files) {
TestSuiteResult result= JAXB.unmarshal(file, TestSuiteResult.class);
names.add(result.getName());}
// Kotlinval names = files.map {
JAXB.unmarshal(it, TestSuiteResult::class.java).name}
_252. Problem: framework can’t do what you need it to do—// Javapublic static void waitForElement(HtmlElement element, longtimeout) {
...}
waitForElement(link, 10);link.click();
_282. Solution: Extension functions—// Kotlinfun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T {
...return this
}
link.waitForIt().click()
loginForm.waitForIt(10).guestButton.waitForIt().click()
_292. Solution: Extension functions—// Kotlinfun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T {
...return this
}
link.waitForIt().click()
loginForm.waitForIt(10).guestButton.waitForIt().click()
_302. Solution: Extension functions—// Kotlinfun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T {
...return this
}
link.waitForIt().click()
loginForm.waitForIt(10).guestButton.waitForIt().click()
_312. Solution: Extension functions—// Kotlinfun <T : HtmlElement> T.waitForIt(timeout: Long = 5): T {
...return this
}
link.waitForIt().click()
loginForm.waitForIt(10).guestButton.waitForIt().click()
• Java (Lombok https://projectlombok.org/)
• Groovy (Extension Modules)
_332. Extension functions: Java & Groovy—
// Javapublic class Credentials {
private final String username;private final String password;
}
_343. Problem: small classes are not small—
// Javapublic class Credentials {
private final String username;private final String password;
public Credentials(String username, String password) {this.username = username;this.password = password;
}}
_353. Problem: small classes are not small—
// Javapublic 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; }
}
_363. Problem: small classes are not small—
// Javapublic 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; }
@Overridepublic String toString() {
return username + '/' + password;}
}
_373. Problem: small classes are not small—
// Javapublic 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; }
@Overridepublic String toString() {
return username + '/' + password;}
@Overridepublic 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;
}
@Overridepublic int hashCode() {
int result = username != null ? username.hashCode() : 0;return 31 * result + (password != null ? password.hashCode() : 0);
}}
_383. Problem: small classes are not small—
// Javapublic 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; }
@Overridepublic String toString() {
return username + '/' + password;}
@Overridepublic 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;
}
@Overridepublic int hashCode() {
int result = username != null ? username.hashCode() : 0;return 31 * result + (password != null ? password.hashCode() : 0);
}}
_393. Problem: 27 lines—
_413. Solution: Kotlin data classes—// Kotlindata class Credentials(val username: String, val password: String)
val creds = Credentials("a", "b")println(creds.username) // acreds.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)
_423. Data classes: Java & Groovy—
// Java@Step("Click the button")public void clickButton() {
driver.findElement("button").click();}
clickButton();
_444. 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();
});
_454. Solution: steps in Allure report—
// Kotlin@Step("{0}")fun step(title: String, code: () -> Any) = code()
step("Click the button") {driver.findElement("button").click()
}
_464. Solution: steps in Allure report—
// Java 8step("Click the button", () -> {
// your code here});
// Kotlinstep("Click the button") {
// your code here}
_474. Solution: just compare—
// Java: String to md5try {
...} 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);}
_505. reportportal/service-api https://git.io/v9RF5—
// Javatry {
screenshotUrl = new URL(screenshotUrl).toExternalForm();} catch (MalformedURLException ignore) { }
_515. codeborne/selenide https://git.io/v9Rbe—
// Javatry {
screenshotUrl = new URL(screenshotUrl).toExternalForm();} catch (MalformedURLException ignore) { }
// KotlinscreenshotUrl = URL(screenshotUrl).toExternalForm();
_525. Solution: no checked exceptions—
// JavaObject errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors instanceof List) {return errorsFromList((List<Object>) errors);
}
_546. Problem: unchecked cast https://git.io/v9Ek1—
// JavaObject errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors instanceof List) {return errorsFromList((List<Object>) errors);
}
_556. Problem: unchecked cast https://git.io/v9Ek1—
// JavaObject 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()));
}
_566. Problem: unchecked cast https://git.io/v9Ek1—
// Kotlinval errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors is List<*>) {return errorsFromList(errors.filterIsInstance<Object>())
}
_576. Solution: Kotlin smart cast—
// Kotlinval errors
= executeJavaScript("return window._selenide_jsErrors");
if (errors is List<*>) {return errorsFromList(errors.filterIsInstance<Object>())
}
_586. Solution: Kotlin smart cast—
// JavaactualText.equals(expectedText)
// KotlinactualText == expectedText
_617. Solution: Kotlin syntactic sugar—
// JavaString s = "date: " + date + " and author: " + USER.getName();String s =
format("date: %s and author: %s", date, USER.getName());
// Kotlinval s = "date: $date and author: ${USER.name}"
_627. Solution: String templates—
var username: Stringusername = null // compilation error
var username: String?username = null // ok
_688. Null safety—
var username: String // non-nullable Stringusername = null
var username: String? // nullable Stringusername = null
_698. Null safety—
var username: String? = "Vasya"var count = username.length // compilation error
_708. Null safety: safe call—
var username: String? = "Vasya"var count = username?.length // ok, count is 5
username = nullvar count = username?.length // ok, count is null
_718. Null safety: safe call—
// Javausername != null ? username : "Guest"
// Kotlinusername ?: "Guest"
var images = findAllImagesOnPage()?: throw AssertionError("No images!")
_738. Null safety: Elvis operator—
?:
• Null safety: Java (Optional<T> in Java8), Groovy• Safe call: Java, Groovy• Elvis operator: Java, Groovy
_748. 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
_76Why not Groovy?—
Kotlin ~ Java—// Kotlinvar counter: Int = 0
// Javaprivate 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
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@Ruleprivate 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@Rulepublic final TemporaryFolder tempFolder = new TemporaryFolder();
1. JUnit 4: @Rule solution #2—
_85
// Kotlin@get:Rule val tempFolder = TemporaryFolder()
// Javaprivate final TemporaryFolder tempFolder = new TemporaryFolder();
@Rulepublic 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—// Kotlinclass 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}") @JvmStaticfun 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
}
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
3. Allure: org.aspectj:aspectjweaver:1.8.10—
_106
// Kotlinlinks.forEach {
println(it.text)}
java.lang.ClassFormatError: Invalid index 6 in LocalVariableTable
3. Allure: org.aspectj:aspectjweaver:1.8.10—
_107
// Kotlinlinks.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
// Kotlinimport com.codeborne.selenide.Selenide.$
Error: Kotlin: Qualified name must be a '.'-separated identifier list
4. Selenide: $ is reserved—
_109
// Kotlinimport com.codeborne.selenide.Selenide.`$`import com.codeborne.selenide.Selenide.`$$`
...
`$`("a")`$$`("a")
5. Selenium WebDriver—
_111
// Kotlinval 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.textval clazz = link.getAttribute("class")Actions(driver).moveToElement(link).perform()(driver as TakesScreenshot).getScreenshotAs(OutputType.BYTES)driver.quit()
6. REST Assured—
_113
// Kotlinfun 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)
_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—
_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—