├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── kotlin
│ │ └── com
│ │ └── fernandocejas
│ │ └── android
│ │ └── sample
│ │ └── ui
│ │ ├── HelloWorldActivityTest.kt
│ │ ├── MainActivityTest.kt
│ │ └── framework
│ │ ├── AcceptanceTest.kt
│ │ ├── Events.kt
│ │ └── Matchers.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── fernandocejas
│ │ │ └── android
│ │ │ └── sample
│ │ │ ├── executor
│ │ │ ├── PostExecutionThread.java
│ │ │ └── ThreadExecutor.java
│ │ │ ├── io
│ │ │ ├── FileManager.java
│ │ │ └── Serializer.java
│ │ │ ├── ui
│ │ │ ├── HelloWorldActivity.java
│ │ │ └── MainActivity.java
│ │ │ └── users
│ │ │ ├── GetUserDetails.java
│ │ │ ├── User.java
│ │ │ ├── UserEntity.java
│ │ │ └── UserRepository.java
│ └── res
│ │ ├── layout
│ │ ├── activity_hello_world.xml
│ │ ├── activity_main.xml
│ │ ├── content_hello_world.xml
│ │ └── content_main.xml
│ │ ├── menu
│ │ └── menu_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── kotlin
│ └── com
│ └── fernandocejas
│ └── android
│ └── sample
│ ├── AndroidTest.kt
│ ├── io
│ ├── FileManagerTest.kt
│ └── SerializerTest.kt
│ └── users
│ └── GetUserDetailsTest.kt
├── build.gradle
├── buildsystem
└── debug.keystore
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows thumbnail db
2 | Thumbs.db
3 |
4 | # OSX files
5 | .DS_Store
6 |
7 | # built application files
8 | *.apk
9 | *.ap_
10 |
11 | # files for the dex VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # generated files
18 | bin/
19 | gen/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Eclipse project files
26 | .classpath
27 | .project
28 |
29 | # Android Studio
30 | .idea
31 | .gradle
32 | /*/local.properties
33 | /*/out
34 | /*/*/build
35 | build
36 | /*/*/production
37 | *.iml
38 | *.iws
39 | *.ipr
40 | *~
41 | *.swp
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Android-KotlinInTests
2 | =========================
3 |
4 | This is a sample app that is part of blog posts I have written about how to test android applications using Kotlin.
5 |
6 | [Android Testing with Kotlin](http://fernandocejas.com/2017/02/03/android-testing-with-kotlin/)
7 |
8 |
9 | ## Local Development
10 |
11 | Here are some useful Gradle/adb commands for executing this example:
12 |
13 | * `./gradlew clean build` - Build the entire example and execute unit and integration tests plus lint check.
14 | * `./gradlew runUnitTests` - Execute both unit and integration tests.
15 | * `./gradlew runAcceptanceTests` - Execute espresso and instrumentation acceptance tests.
16 |
17 |
18 |
19 | ## Code style
20 |
21 | Here you can download and install the java codestyle.
22 | https://github.com/android10/java-code-styles
23 |
24 |
25 | ## License
26 |
27 | Copyright 2018 Fernando Cejas
28 |
29 | Licensed under the Apache License, Version 2.0 (the "License");
30 | you may not use this file except in compliance with the License.
31 | You may obtain a copy of the License at
32 |
33 | http://www.apache.org/licenses/LICENSE-2.0
34 |
35 | Unless required by applicable law or agreed to in writing, software
36 | distributed under the License is distributed on an "AS IS" BASIS,
37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38 | See the License for the specific language governing permissions and
39 | limitations under the License.
40 |
41 |
42 | 
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.0.5-2'
8 | }
9 | }
10 |
11 | apply plugin: 'com.android.application'
12 | apply plugin: 'kotlin-android'
13 |
14 | android {
15 | compileSdkVersion 25
16 | buildToolsVersion '25.0.2'
17 |
18 | defaultConfig {
19 | applicationId 'com.fernandocejas.android.sample'
20 | minSdkVersion 15
21 | targetSdkVersion 25
22 | versionCode 1
23 | versionName "1.0"
24 | testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
25 | }
26 |
27 | sourceSets {
28 | //Dedicated directories for tests written in Kotlin
29 | test.java.srcDirs += 'src/test/kotlin'
30 | androidTest.java.srcDirs += 'src/androidTest/kotlin'
31 | }
32 |
33 | packagingOptions {
34 | exclude 'LICENSE.txt'
35 | exclude 'META-INF/DEPENDENCIES'
36 | exclude 'META-INF/ASL2.0'
37 | exclude 'META-INF/NOTICE'
38 | exclude 'META-INF/LICENSE'
39 | }
40 |
41 | lintOptions {
42 | quiet true
43 | abortOnError false
44 | ignoreWarnings true
45 | disable 'InvalidPackage' //Some libraries have issues with this.
46 | disable 'OldTargetApi' //Lint gives this warning but SDK 20 would be Android L Beta.
47 | disable 'IconDensities' //For testing purpose. This is safe to remove.
48 | disable 'IconMissingDensityFolder' //For testing purpose. This is safe to remove.
49 | }
50 |
51 | signingConfigs {
52 | debug {
53 | storeFile file('../buildsystem/debug.keystore')
54 | storePassword 'android'
55 | keyAlias 'androiddebugkey'
56 | keyPassword 'android'
57 | }
58 | }
59 |
60 | buildTypes {
61 | debug {
62 | signingConfig signingConfigs.debug
63 | }
64 | }
65 | }
66 |
67 | //Ensure Kotlin will not be used in production code.
68 | afterEvaluate {
69 | android.sourceSets.all { sourceSet ->
70 | if (!sourceSet.name.startsWith('test') || !sourceSet.name.startsWith('androidTest')) {
71 | sourceSet.kotlin.setSrcDirs([])
72 | }
73 | }
74 | }
75 |
76 | dependencies {
77 | compile 'io.reactivex.rxjava2:rxjava:2.0.2'
78 | compile 'com.google.code.gson:gson:2.4'
79 | compile 'com.android.support:appcompat-v7:25.1.0'
80 | compile 'com.android.support:design:25.1.0'
81 | compile "org.jetbrains.kotlin:kotlin-stdlib:1.0.6"
82 |
83 | testCompile 'junit:junit:4.12'
84 | testCompile "org.robolectric:robolectric:3.2.1"
85 | testCompile 'org.jetbrains.kotlin:kotlin-stdlib:1.0.6'
86 | testCompile 'org.jetbrains.kotlin:kotlin-test-junit:1.0.6'
87 | testCompile "com.nhaarman:mockito-kotlin:1.1.0"
88 | testCompile 'org.amshove.kluent:kluent:1.14'
89 |
90 | androidTestCompile 'com.android.support.test:runner:0.5'
91 | androidTestCompile 'com.android.support.test:rules:0.5'
92 | androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
93 | androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
94 | androidTestCompile 'com.android.support:support-annotations:25.1.0'
95 | }
96 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/23.0.2/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/fernandocejas/android/sample/ui/HelloWorldActivityTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui
17 |
18 | import com.fernandocejas.android.sample.R
19 | import com.fernandocejas.android.sample.ui.framework.AcceptanceTest
20 | import org.junit.Test
21 |
22 | class HelloWorldActivityTest : AcceptanceTest(HelloWorldActivity::class.java) {
23 |
24 | @Test
25 | fun shouldSayHelloWorld() {
26 | checkThat.viewContainsText(R.id.hello, R.string.hello)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/fernandocejas/android/sample/ui/MainActivityTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui
17 |
18 | import com.fernandocejas.android.sample.R
19 | import com.fernandocejas.android.sample.ui.framework.AcceptanceTest
20 | import org.junit.Test
21 |
22 | class MainActivityTest : AcceptanceTest(MainActivity::class.java) {
23 |
24 | @Test
25 | fun shouldOpenHelloWorldScreen() {
26 | events.clickOnView(R.id.btn_hello_world)
27 | checkThat.nextOpenActivityIs(HelloWorldActivity::class.java)
28 | }
29 |
30 | @Test
31 | fun shouldDisplayAction() {
32 | events.clickOnView(R.id.fab)
33 | checkThat.viewIsVisibleAndContainsText(R.string.action)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/fernandocejas/android/sample/ui/framework/AcceptanceTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui.framework
17 |
18 | import android.app.Activity
19 | import android.support.test.espresso.intent.rule.IntentsTestRule
20 | import android.support.test.filters.LargeTest
21 | import android.support.test.rule.ActivityTestRule
22 | import android.support.test.runner.AndroidJUnit4
23 | import org.junit.Rule
24 | import org.junit.runner.RunWith
25 |
26 | @LargeTest
27 | @RunWith(AndroidJUnit4::class)
28 | abstract class AcceptanceTest(clazz: Class) {
29 |
30 | @Rule @JvmField
31 | val testRule: ActivityTestRule = IntentsTestRule(clazz)
32 |
33 | val checkThat: Matchers = Matchers()
34 | val events: Events = Events()
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/fernandocejas/android/sample/ui/framework/Events.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui.framework
17 |
18 | import android.support.annotation.IdRes
19 | import android.support.test.espresso.Espresso.onView
20 | import android.support.test.espresso.action.ViewActions.click
21 | import android.support.test.espresso.matcher.ViewMatchers.withId
22 |
23 | class Events {
24 | fun clickOnView(@IdRes viewId: Int) {
25 | onView(withId(viewId)).perform(click())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/androidTest/kotlin/com/fernandocejas/android/sample/ui/framework/Matchers.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui.framework
17 |
18 | import android.app.Activity
19 | import android.support.annotation.IdRes
20 | import android.support.annotation.StringRes
21 | import android.support.test.espresso.Espresso.onView
22 | import android.support.test.espresso.assertion.ViewAssertions.matches
23 | import android.support.test.espresso.intent.Intents.intended
24 | import android.support.test.espresso.intent.matcher.IntentMatchers
25 | import android.support.test.espresso.matcher.ViewMatchers.*
26 |
27 | class Matchers {
28 | fun nextOpenActivityIs(clazz: Class) {
29 | intended(IntentMatchers.hasComponent(clazz.name))
30 | }
31 |
32 | fun viewIsVisibleAndContainsText(@StringRes stringResource: Int) {
33 | onView(withText(stringResource)).check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
34 | }
35 |
36 | fun viewContainsText(@IdRes viewId: Int, @StringRes stringResource: Int) {
37 | onView(withId(viewId)).check(matches(withText(stringResource)))
38 | }
39 | }
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/executor/PostExecutionThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.executor;
17 |
18 | import io.reactivex.Scheduler;
19 |
20 | /**
21 | * Thread abstraction created to change the execution context from any thread to any other thread.
22 | * Useful to encapsulate a UI Thread for example, since some job will be done in background, an
23 | * implementation of this interface will change context and update the UI.
24 | */
25 | public abstract class PostExecutionThread {
26 | public abstract Scheduler getScheduler();
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/executor/ThreadExecutor.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.executor;
17 |
18 | import java.util.concurrent.Executor;
19 |
20 | /**
21 | * Executor implementation can be based on different frameworks or techniques
22 | * of asynchronous execution.
23 | */
24 | public interface ThreadExecutor extends Executor {}
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/io/FileManager.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.io;
17 |
18 | import android.content.Context;
19 | import android.content.SharedPreferences;
20 |
21 | import java.io.BufferedReader;
22 | import java.io.File;
23 | import java.io.FileReader;
24 | import java.io.FileWriter;
25 | import java.io.IOException;
26 |
27 | /**
28 | * Helper class to do operations on regular files/directories.
29 | */
30 | public class FileManager {
31 |
32 | FileManager() {}
33 |
34 | /**
35 | * Writes a file to Disk.
36 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
37 | * perform this operation using another thread.
38 | *
39 | * @param file The file to write to Disk.
40 | */
41 | void writeToFile(File file, String fileContent) {
42 | if (!file.exists()) {
43 | try {
44 | final FileWriter writer = new FileWriter(file);
45 | writer.write(fileContent);
46 | writer.close();
47 | } catch (IOException e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | }
52 |
53 | /**
54 | * Reads a content from a file.
55 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
56 | * perform the operation using another thread.
57 | *
58 | * @param file The file to read from.
59 | * @return A string with the content of the file.
60 | */
61 | String readFileContent(File file) {
62 | final StringBuilder fileContentBuilder = new StringBuilder();
63 | if (file.exists()) {
64 | String stringLine;
65 | try {
66 | final FileReader fileReader = new FileReader(file);
67 | final BufferedReader bufferedReader = new BufferedReader(fileReader);
68 | while ((stringLine = bufferedReader.readLine()) != null) {
69 | fileContentBuilder.append(stringLine).append("\n");
70 | }
71 | bufferedReader.close();
72 | fileReader.close();
73 | } catch (IOException e) {
74 | e.printStackTrace();
75 | }
76 | }
77 | return fileContentBuilder.toString();
78 | }
79 |
80 | /**
81 | * Returns a boolean indicating whether this file can be found on the underlying file system.
82 | *
83 | * @param file The file to check existence.
84 | * @return true if this file exists, false otherwise.
85 | */
86 | boolean exists(File file) {
87 | return file.exists();
88 | }
89 |
90 | /**
91 | * Warning: Deletes the content of a directory.
92 | * This is an I/O operation and this method executes in the main thread, so it is recommended to
93 | * perform the operation using another thread.
94 | *
95 | * @param directory The directory which its content will be deleted.
96 | */
97 | boolean clearDirectory(File directory) {
98 | boolean result = false;
99 | if (directory.exists()) {
100 | for (File file : directory.listFiles()) {
101 | result = file.delete();
102 | }
103 | }
104 | return result;
105 | }
106 |
107 | /**
108 | * Write a value to a user preferences file.
109 | *
110 | * @param context {@link android.content.Context} to retrieve android user preferences.
111 | * @param preferenceFileName A file name reprensenting where data will be written to.
112 | * @param key A string for the key that will be used to retrieve the value in the future.
113 | * @param value A long representing the value to be inserted.
114 | */
115 | void writeToPreferences(Context context, String preferenceFileName, String key,
116 | long value) {
117 |
118 | final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
119 | Context.MODE_PRIVATE);
120 | final SharedPreferences.Editor editor = sharedPreferences.edit();
121 | editor.putLong(key, value);
122 | editor.apply();
123 | }
124 |
125 | /**
126 | * Get a value from a user preferences file.
127 | *
128 | * @param context {@link android.content.Context} to retrieve android user preferences.
129 | * @param preferenceFileName A file name representing where data will be get from.
130 | * @param key A key that will be used to retrieve the value from the preference file.
131 | * @return A long representing the value retrieved from the preferences file.
132 | */
133 | long getFromPreferences(Context context, String preferenceFileName, String key) {
134 | final SharedPreferences sharedPreferences = context.getSharedPreferences(preferenceFileName,
135 | Context.MODE_PRIVATE);
136 | return sharedPreferences.getLong(key, 0);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/io/Serializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.io;
17 |
18 | import com.google.gson.Gson;
19 |
20 | /**
21 | * Json Serializer/Deserializer.
22 | */
23 | public class Serializer {
24 |
25 | private final Gson gson = new Gson();
26 |
27 | Serializer() {}
28 |
29 | /**
30 | * Serialize an object to Json.
31 | *
32 | * @param object to serialize.
33 | */
34 | public String serialize(Object object, Class clazz) {
35 | return gson.toJson(object, clazz);
36 | }
37 |
38 | /**
39 | * Deserialize a json representation of an object.
40 | *
41 | * @param string A json string to deserialize.
42 | */
43 | public T deserialize(String string, Class clazz) {
44 | return gson.fromJson(string, clazz);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/ui/HelloWorldActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui;
17 |
18 | import android.os.Bundle;
19 | import android.support.v7.app.AppCompatActivity;
20 | import android.support.v7.widget.Toolbar;
21 | import com.fernandocejas.android.sample.R;
22 |
23 | public class HelloWorldActivity extends AppCompatActivity {
24 |
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | setContentView(R.layout.activity_hello_world);
29 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
30 | setSupportActionBar(toolbar);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.ui;
17 |
18 | import android.content.Intent;
19 | import android.os.Bundle;
20 | import android.support.design.widget.FloatingActionButton;
21 | import android.support.design.widget.Snackbar;
22 | import android.support.v7.app.AppCompatActivity;
23 | import android.support.v7.widget.Toolbar;
24 | import android.view.Menu;
25 | import android.view.MenuItem;
26 | import android.view.View;
27 | import android.widget.Button;
28 | import com.fernandocejas.android.sample.R;
29 |
30 | public class MainActivity extends AppCompatActivity {
31 |
32 | @Override
33 | public boolean onCreateOptionsMenu(Menu menu) {
34 | getMenuInflater().inflate(R.menu.menu_main, menu);
35 | return true;
36 | }
37 |
38 | @Override
39 | public boolean onOptionsItemSelected(MenuItem item) {
40 | int id = item.getItemId();
41 | //noinspection SimplifiableIfStatement
42 | if (id == R.id.action_settings) {
43 | return true;
44 | }
45 | return super.onOptionsItemSelected(item);
46 | }
47 |
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_main);
52 |
53 | //Toolbar
54 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
55 | setSupportActionBar(toolbar);
56 |
57 | //Floating button
58 | final FloatingActionButton floatingActionButton = (FloatingActionButton) findViewById(R.id.fab);
59 | floatingActionButton.setOnClickListener(new View.OnClickListener() {
60 | @Override
61 | public void onClick(View view) {
62 | Snackbar.make(view, R.string.action, Snackbar.LENGTH_LONG)
63 | .setAction("Action", null).show();
64 | }
65 | });
66 |
67 | //Hello world button
68 | final Button helloWorldButton = (Button) findViewById(R.id.btn_hello_world);
69 | helloWorldButton.setOnClickListener(new View.OnClickListener() {
70 | @Override
71 | public void onClick(View view) {
72 | startActivity(new Intent(MainActivity.this, HelloWorldActivity.class));
73 | }
74 | });
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/users/GetUserDetails.java:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android.sample.users;
2 |
3 | import com.fernandocejas.android.sample.executor.PostExecutionThread;
4 | import com.fernandocejas.android.sample.executor.ThreadExecutor;
5 | import io.reactivex.Observable;
6 |
7 | /**
8 | * Copyright (C) 2017 android10.org Open Source Project
9 | *
10 | * Licensed under the Apache License, Version 2.0 (the "License");
11 | * you may not use this file except in compliance with the License.
12 | * You may obtain a copy of the License at
13 | *
14 | * http://www.apache.org/licenses/LICENSE-2.0
15 | *
16 | * Unless required by applicable law or agreed to in writing, software
17 | * distributed under the License is distributed on an "AS IS" BASIS,
18 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | * See the License for the specific language governing permissions and
20 | * limitations under the License.
21 | */
22 | public class GetUserDetails {
23 |
24 | private final UserRepository userRepository;
25 | private final ThreadExecutor threadExecutor;
26 | private final PostExecutionThread postExecutionThread;
27 |
28 | GetUserDetails(UserRepository userRepository, ThreadExecutor threadExecutor,
29 | PostExecutionThread postExecutionThread) {
30 | this.userRepository = userRepository;
31 | this.threadExecutor = threadExecutor;
32 | this.postExecutionThread = postExecutionThread;
33 | }
34 |
35 | Observable buildUseCaseObservable(Params params) {
36 | return this.userRepository.user(params.userId);
37 | }
38 |
39 | static final class Params {
40 |
41 | private final int userId;
42 |
43 | private Params(int userId) {
44 | this.userId = userId;
45 | }
46 |
47 | public static Params forUser(int userId) {
48 | return new Params(userId);
49 | }
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/users/User.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.users;
17 |
18 | /**
19 | * Class that represents a User in the domain layer.
20 | */
21 | public class User {
22 |
23 | private final int userId;
24 |
25 | public User(int userId) {
26 | this.userId = userId;
27 | }
28 |
29 | private String coverUrl;
30 | private String fullName;
31 | private String email;
32 | private String description;
33 | private int followers;
34 |
35 | public int getUserId() {
36 | return userId;
37 | }
38 |
39 | public String getCoverUrl() {
40 | return coverUrl;
41 | }
42 |
43 | public void setCoverUrl(String coverUrl) {
44 | this.coverUrl = coverUrl;
45 | }
46 |
47 | public String getFullName() {
48 | return fullName;
49 | }
50 |
51 | public void setFullName(String fullName) {
52 | this.fullName = fullName;
53 | }
54 |
55 | public String getEmail() {
56 | return email;
57 | }
58 |
59 | public void setEmail(String email) {
60 | this.email = email;
61 | }
62 |
63 | public String getDescription() {
64 | return description;
65 | }
66 |
67 | public void setDescription(String description) {
68 | this.description = description;
69 | }
70 |
71 | public int getFollowers() {
72 | return followers;
73 | }
74 |
75 | public void setFollowers(int followers) {
76 | this.followers = followers;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/users/UserEntity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.users;
17 |
18 | import com.google.gson.annotations.SerializedName;
19 |
20 | /**
21 | * User Entity used in the data layer.
22 | */
23 | public class UserEntity {
24 |
25 | @SerializedName("id")
26 | private int userId;
27 |
28 | @SerializedName("cover_url")
29 | private String coverUrl;
30 |
31 | @SerializedName("full_name")
32 | private String fullname;
33 |
34 | @SerializedName("description")
35 | private String description;
36 |
37 | @SerializedName("followers")
38 | private int followers;
39 |
40 | @SerializedName("email")
41 | private String email;
42 |
43 | public UserEntity() {
44 | //empty
45 | }
46 |
47 | public int getUserId() {
48 | return userId;
49 | }
50 |
51 | public void setUserId(int userId) {
52 | this.userId = userId;
53 | }
54 |
55 | public String getCoverUrl() {
56 | return coverUrl;
57 | }
58 |
59 | public String getFullname() {
60 | return fullname;
61 | }
62 |
63 | public void setFullname(String fullname) {
64 | this.fullname = fullname;
65 | }
66 |
67 | public String getDescription() {
68 | return description;
69 | }
70 |
71 | public int getFollowers() {
72 | return followers;
73 | }
74 |
75 | public String getEmail() {
76 | return email;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fernandocejas/android/sample/users/UserRepository.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2015 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.users;
17 |
18 | import io.reactivex.Observable;
19 | import java.util.List;
20 |
21 | /**
22 | * Interface that represents a Repository for getting {@link User} related data.
23 | */
24 | public interface UserRepository {
25 | /**
26 | * Get an {@link Observable} which will emit a List of {@link User}.
27 | */
28 | Observable> users();
29 |
30 | /**
31 | * Get an {@link Observable} which will emit a {@link User}.
32 | *
33 | * @param userId The user id used to retrieve user data.
34 | */
35 | Observable user(final int userId);
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_hello_world.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_hello_world.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
22 |
23 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android-KotlinInTests
3 | Settings
4 | Hello World Test
5 | This is an action
6 | Hello World!!!
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/com/fernandocejas/android/sample/AndroidTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample
17 |
18 | import android.app.Application
19 | import android.content.Context
20 | import org.junit.runner.RunWith
21 | import org.robolectric.RobolectricTestRunner
22 | import org.robolectric.RuntimeEnvironment
23 | import org.robolectric.annotation.Config
24 | import java.io.File
25 |
26 | /**
27 | * Base class for Robolectric data layer tests.
28 | * Inherit from this class to create a test.
29 | */
30 | @RunWith(RobolectricTestRunner::class)
31 | @Config(constants = BuildConfig::class,
32 | application = AndroidTest.ApplicationStub::class,
33 | sdk = intArrayOf(21))
34 | abstract class AndroidTest {
35 |
36 | fun context(): Context {
37 | return RuntimeEnvironment.application
38 | }
39 |
40 | fun cacheDir(): File {
41 | return context().cacheDir
42 | }
43 |
44 | internal class ApplicationStub : Application()
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/com/fernandocejas/android/sample/io/FileManagerTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.io
17 |
18 | import com.fernandocejas.android.sample.AndroidTest
19 | import org.amshove.kluent.shouldEqualTo
20 | import org.junit.After
21 | import org.junit.Test
22 | import java.io.File
23 |
24 | class FileManagerTest : AndroidTest() {
25 |
26 | private var fileManager = FileManager()
27 |
28 | @After
29 | fun tearDown() {
30 | fileManager.clearDirectory(cacheDir())
31 | }
32 |
33 | @Test
34 | fun shouldWriteToFile() {
35 | val fileToWrite = createDummyFile()
36 | val fileContent = "content"
37 |
38 | fileManager.writeToFile(fileToWrite, fileContent)
39 |
40 | fileToWrite.exists() shouldEqualTo true
41 | }
42 |
43 | @Test
44 | fun shouldHaveCorrectFileContent() {
45 | val fileToWrite = createDummyFile()
46 | val fileContent = "content\n"
47 |
48 | fileManager.writeToFile(fileToWrite, fileContent)
49 | val expectedContent = fileManager.readFileContent(fileToWrite)
50 |
51 | expectedContent shouldEqualTo fileContent
52 | }
53 |
54 | private fun createDummyFile(): File {
55 | val dummyFilePath = cacheDir().path + File.separator + "dummyFile"
56 | return File(dummyFilePath)
57 | }
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/com/fernandocejas/android/sample/io/SerializerTest.kt:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2017 Fernando Cejas Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.fernandocejas.android.sample.io
17 |
18 | import com.fernandocejas.android.sample.users.UserEntity
19 | import org.amshove.kluent.shouldEqual
20 | import org.junit.Test
21 |
22 | class SerializerTest {
23 |
24 | private val JSON_RESPONSE = "{\n \"id\": 1,\n " +
25 | "\"cover_url\": \"http://www.android10.org/myapi/cover_1.jpg\",\n " +
26 | "\"full_name\": \"Simon Hill\",\n " +
27 | "\"description\": \"Curabitur gravida nisi at nibh. In hac habitasse " +
28 | "platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer " +
29 | "eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. " +
30 | "Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo " +
31 | "placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, " +
32 | "lacinia eget, tincidunt eget, tempus vel, pede.\",\n " +
33 | "\"followers\": 7484,\n " +
34 | "\"email\": \"jcooper@babbleset.edu\"\n}"
35 |
36 | private var serializer = Serializer()
37 |
38 | @Test
39 | fun shouldSerialize() {
40 | val userEntityOne = serializer.deserialize(JSON_RESPONSE, UserEntity::class.java)
41 | val jsonString = serializer.serialize(userEntityOne, UserEntity::class.java)
42 | val userEntityTwo = serializer.deserialize(jsonString, UserEntity::class.java)
43 |
44 | userEntityOne.userId shouldEqual userEntityTwo.userId
45 | userEntityOne.fullname shouldEqual userEntityTwo.fullname
46 | userEntityOne.followers shouldEqual userEntityTwo.followers
47 | }
48 |
49 | @Test
50 | fun shouldDesearialize() {
51 | val userEntity = serializer.deserialize(JSON_RESPONSE, UserEntity::class.java)
52 |
53 | userEntity.userId shouldEqual 1
54 | userEntity.fullname shouldEqual "Simon Hill"
55 | userEntity.followers shouldEqual 7484
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/app/src/test/kotlin/com/fernandocejas/android/sample/users/GetUserDetailsTest.kt:
--------------------------------------------------------------------------------
1 | package com.fernandocejas.android.sample.users
2 |
3 | import com.fernandocejas.android.sample.executor.PostExecutionThread
4 | import com.fernandocejas.android.sample.executor.ThreadExecutor
5 | import com.nhaarman.mockito_kotlin.mock
6 | import com.nhaarman.mockito_kotlin.verify
7 | import com.nhaarman.mockito_kotlin.verifyNoMoreInteractions
8 | import org.junit.Before
9 | import org.junit.Test
10 | import org.mockito.Mockito.verifyZeroInteractions
11 |
12 | class GetUserDetailsTest {
13 |
14 | private val USER_ID = 123
15 |
16 | private lateinit var getUserDetails: GetUserDetails
17 |
18 | private val userRepository: UserRepository = mock()
19 | private val threadExecutor: ThreadExecutor = mock()
20 | private val postExecutionThread: PostExecutionThread = mock()
21 |
22 | @Before
23 | fun setUp() {
24 | getUserDetails = GetUserDetails(userRepository, threadExecutor, postExecutionThread)
25 | }
26 |
27 | @Test
28 | fun shouldGetUserDetails() {
29 | getUserDetails.buildUseCaseObservable(GetUserDetails.Params.forUser(USER_ID));
30 |
31 | verify(userRepository).user(USER_ID)
32 | verifyNoMoreInteractions(userRepository)
33 | verifyZeroInteractions(postExecutionThread)
34 | verifyZeroInteractions(threadExecutor)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:2.2.2'
7 | }
8 | }
9 |
10 | allprojects {
11 | repositories {
12 | jcenter()
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
20 | task runUnitTests(dependsOn: [':app:testDebugUnitTest']) {
21 | description 'Run all unit tests'
22 | }
23 |
24 | task runAcceptanceTests(dependsOn: [':app:connectedAndroidTest']) {
25 | description 'Run all acceptance tests.'
26 | }
27 |
--------------------------------------------------------------------------------
/buildsystem/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/buildsystem/debug.keystore
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 -XX:MaxPermSize=512m -Xmx4608M
2 | org.gradle.parallel=true
3 | org.gradle.configureondemand=true
4 | kotlin.incremental=true
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/android10/Android-KotlinInTests/116b0b889cc0746bc1383c4adb4961ac073dead0/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jan 08 19:14:53 ART 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn ( ) {
37 | echo "$*"
38 | }
39 |
40 | die ( ) {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
166 | cd "$(dirname "$0")"
167 | fi
168 |
169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
170 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------