Skip to content

Jqwik failing due to AnnotatedType #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ plugins {
}

android {
compileSdkVersion 29
compileSdkVersion 31
buildToolsVersion "29.0.3"

defaultConfig {
applicationId "sergio.sastre.multiplying.quality.of.unittests"
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 31
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

}

buildTypes {
Expand Down Expand Up @@ -47,6 +48,13 @@ android {
include '**/*Examples.class'
}
}
buildFeatures {
viewBinding true
}

packagingOptions {
exclude "META-INF/*"
}
}

dependencies {
Expand All @@ -55,13 +63,36 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.annotation:annotation:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'

testImplementation 'junit:junit:4.13.2'
testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1"
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:5.7.1"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.1"
testImplementation "org.junit.jupiter:junit-jupiter-params:5.8.1"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:5.8.1"
testImplementation "com.google.truth:truth:1.1.2"

testImplementation("net.jqwik:jqwik:1.6.0")
testImplementation("net.jqwik:jqwik-kotlin:1.6.0")

// Core library
androidTestImplementation 'androidx.test:core:1.4.0'

// AndroidJUnitRunner and JUnit Rules
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'

// Espresso dependencies
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

// Jqwik for instrumented tests
androidTestImplementation("net.jqwik:jqwik:1.6.0")
androidTestImplementation("net.jqwik:jqwik-kotlin:1.6.0")

// Jqwik requires Junit5
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation("org.junit.platform:junit-platform-runner:1.8.1")

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package sergio.sastre.multiplying.quality.of.unittests.ui.login

import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.matcher.ViewMatchers.*
import net.jqwik.api.*
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf
import org.hamcrest.TypeSafeMatcher
import org.junit.runner.RunWith
import org.junit.platform.runner.JUnitPlatform
import sergio.sastre.multiplying.quality.of.unittests.R

@RunWith(JUnitPlatform::class)
class LoginActivityTest2 {

@Provide
fun noUpperCase(): Arbitrary<String> =
Arbitraries.strings().ascii().filter { it.matches("[^A-Z]".toRegex()) }

@Property(tries = 1)
fun loginActivityTest(@ForAll("noUpperCase") password: String?) {

val appCompatEditText = onView(
allOf(
withId(R.id.username),
childAtPosition(
allOf(
withId(R.id.container),
childAtPosition(
withId(android.R.id.content),
0
)
),
0
),
isDisplayed()
)
)
appCompatEditText.perform(replaceText(password), closeSoftKeyboard())

val appCompatEditText2 = onView(
allOf(
withId(R.id.username), withText("hello"),
childAtPosition(
allOf(
withId(R.id.container),
childAtPosition(
withId(android.R.id.content),
0
)
),
0
),
isDisplayed()
)
)
appCompatEditText2.perform(click())
}


private fun childAtPosition(
parentMatcher: Matcher<View>, position: Int
): Matcher<View> {

return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("Child at position $position in parent ")
parentMatcher.describeTo(description)
}

public override fun matchesSafely(view: View): Boolean {
val parent = view.parent
return parent is ViewGroup && parentMatcher.matches(parent)
&& view == parent.getChildAt(position)
}
}
}
}
12 changes: 11 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MultiplyingTheQualityOfUnitTests" />
android:theme="@style/Theme.MultiplyingTheQualityOfUnitTests">
<activity
android:name=".ui.login.LoginActivity"
android:exported="true"
android:label="@string/title_activity_login">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sergio.sastre.multiplying.quality.of.unittests.data

import sergio.sastre.multiplying.quality.of.unittests.data.model.LoggedInUser
import java.io.IOException

/**
* Class that handles authentication w/ login credentials and retrieves user information.
*/
class LoginDataSource {

fun login(username: String, password: String): Result<LoggedInUser> {
try {
// TODO: handle loggedInUser authentication
val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe")
return Result.Success(fakeUser)
} catch (e: Throwable) {
return Result.Error(IOException("Error logging in", e))
}
}

fun logout() {
// TODO: revoke authentication
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package sergio.sastre.multiplying.quality.of.unittests.data

import sergio.sastre.multiplying.quality.of.unittests.data.model.LoggedInUser

/**
* Class that requests authentication and user information from the remote data source and
* maintains an in-memory cache of login status and user credentials information.
*/

class LoginRepository(val dataSource: LoginDataSource) {

// in-memory cache of the loggedInUser object
var user: LoggedInUser? = null
private set

val isLoggedIn: Boolean
get() = user != null

init {
// If user credentials will be cached in local storage, it is recommended it be encrypted
// @see https://developer.android.com/training/articles/keystore
user = null
}

fun logout() {
user = null
dataSource.logout()
}

fun login(username: String, password: String): Result<LoggedInUser> {
// handle login
val result = dataSource.login(username, password)

if (result is Result.Success) {
setLoggedInUser(result.data)
}

return result
}

private fun setLoggedInUser(loggedInUser: LoggedInUser) {
this.user = loggedInUser
// If user credentials will be cached in local storage, it is recommended it be encrypted
// @see https://developer.android.com/training/articles/keystore
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package sergio.sastre.multiplying.quality.of.unittests.data

/**
* A generic class that holds a value with its loading status.
* @param <T>
*/
sealed class Result<out T : Any> {

data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()

override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sergio.sastre.multiplying.quality.of.unittests.data.model

/**
* Data class that captures user information for logged in users retrieved from LoginRepository
*/
data class LoggedInUser(
val userId: String,
val displayName: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sergio.sastre.multiplying.quality.of.unittests.ui.login

/**
* User details post authentication that is exposed to the UI
*/
data class LoggedInUserView(
val displayName: String
//... other data fields that may be accessible to the UI
)
Loading