Skip to content
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

Move TestLiveDataObserver to separate module #93

Merged
merged 3 commits into from
Jul 9, 2018
Merged
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
1 change: 1 addition & 0 deletions client-data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies {

compile 'com.jakewharton.timber:timber:4.7.0'

testImplementation project(':livedata-testing')
testCompile 'junit:junit:4.12'
testCompile 'org.assertj:assertj-core:3.10.0'
testCompile 'org.mockito:mockito-core:2.19.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,13 @@
package com.jraska.github.client

import android.arch.lifecycle.*
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.Observer
import com.jraska.livedata.TestLiveDataObserver

class TestLiveDateObserver<T> : Observer<T>, LifecycleOwner {
private val fakeRegistry = LifecycleRegistry(this)
private val values = ArrayList<T?>()

override fun getLifecycle(): Lifecycle {
return fakeRegistry
}

override fun onChanged(t: T?) {
values.add(t)
}

fun value(): T {
return values.last()!!
}

fun dispose() {
markState(Lifecycle.State.DESTROYED)
}

fun markState(state: Lifecycle.State): TestLiveDateObserver<T> {
fakeRegistry.markState(state)
return this
}
fun <T> LiveData<T>.test(): TestLiveDataObserver<T> {
return TestLiveDataObserver.test(this)
}

fun <T> LiveData<T>.test(): TestLiveDateObserver<T> {
val observer = TestLiveDateObserver<T>()
observe(observer, observer)
observer.markState(Lifecycle.State.STARTED)
return observer
fun <T> LiveData<T>.test(delegate: Observer<T>): TestLiveDataObserver<T> {
return TestLiveDataObserver.test(this, delegate)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import com.jraska.github.client.Navigator
import com.jraska.github.client.analytics.EventAnalytics
import com.jraska.github.client.test
import io.reactivex.Single
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -35,18 +34,23 @@ class UsersViewModelTest {

@Test
fun refreshesProperly() {
assertThat(viewModel.users().test().value().result()).isEmpty()
val testObserver = viewModel.users()
.test()
.assertValue { it.result()!!.isEmpty() }
.assertValueCount(2)

users.add(GitHubUser().apply {
login = "jraska"
avatarUrl = "https://gihub.com/jraska/avatar.png"
htmlUrl = "https://github.com/jraska"
})

assertThat(viewModel.users().test().value().result()).isEmpty()
testObserver.assertValue { it.result()!!.isEmpty() }

viewModel.onRefresh()

assertThat(viewModel.users().test().value().result()!!.first().login).isEqualTo("jraska")
testObserver.assertNever { it == null }
.assertValue { it.result()!!.first().login == "jraska" }
.assertValueCount(4)
}
}
1 change: 1 addition & 0 deletions livedata-testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
23 changes: 23 additions & 0 deletions livedata-testing/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 21
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
implementation 'com.android.support:support-annotations:27.1.1'
implementation 'android.arch.lifecycle:runtime:1.1.1'
implementation 'android.arch.lifecycle:livedata-core:1.1.1'

testImplementation 'junit:junit:4.12'
testImplementation 'android.arch.core:core-testing:1.1.1'
}
1 change: 1 addition & 0 deletions livedata-testing/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="com.jraska.livedata.testing" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.jraska.livedata;

import android.arch.core.util.Function;
import android.arch.lifecycle.*;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class TestLiveDataObserver<T> implements Observer<T> {
private static final Observer<Object> EMPTY_OBSERVER = t -> {
};

private final FakeOwner fakeOwner = new FakeOwner();
private final List<T> valuesHistory = new ArrayList<>();
private final Observer<? super T> delegate;

private TestLiveDataObserver() {
this(EMPTY_OBSERVER);
}

private TestLiveDataObserver(Observer<? super T> delegate) {
this.delegate = delegate;
}

@Override public void onChanged(@Nullable T value) {
valuesHistory.add(value);
delegate.onChanged(value);
}

@NonNull public LifecycleOwner lifecycleOwner() {
return fakeOwner;
}

public TestLiveDataObserver<T> markState(Lifecycle.State state) {
fakeOwner.fakeRegistry.markState(state);
return this;
}

public T value() {
assertHasValue();
return valuesHistory.get(valuesHistory.size() - 1);
}

public List<T> valuesHistory() {
return Collections.unmodifiableList(new ArrayList<>(valuesHistory));
}

public TestLiveDataObserver<T> dispose() {
valuesHistory.clear();
return markState(Lifecycle.State.DESTROYED);
}

public TestLiveDataObserver<T> assertHasValue() {
if (valuesHistory.isEmpty()) {
throw fail("Observer never received any value");
}

return this;
}

public TestLiveDataObserver<T> assertNoValues() {
return assertValueCount(0);
}

public TestLiveDataObserver<T> assertValueCount(int count) {
int size = valuesHistory.size();
if (size != count) {
throw fail("Value counts differ; Expected: " + count + ", Actual: " + size);
}
return this;
}

public TestLiveDataObserver<T> assertValue(T expected) {
T value = value();

if (expected == null && value == null) {
return this;
}

if (!value.equals(expected)) {
throw fail("Expected: " + valueAndClass(expected) + ", Actual: " + valueAndClass(value));
}

return this;
}

public TestLiveDataObserver<T> assertValue(Function<T, Boolean> valuePredicate) {
T value = value();

if (!valuePredicate.apply(value)) {
throw fail("Value not present");
}

return this;
}

public TestLiveDataObserver<T> assertNever(Function<T, Boolean> valuePredicate) {
int size = valuesHistory.size();
for (int valueIndex = 0; valueIndex < size; valueIndex++) {
T value = this.valuesHistory.get(valueIndex);
if (valuePredicate.apply(value)) {
throw fail("Value at position " + valueIndex + " matches predicate "
+ valuePredicate.toString() + ", which was not expected.");
}
}

return this;
}

private AssertionError fail(String message) {
return new AssertionError(message);
}

private static String valueAndClass(Object value) {
if (value != null) {
return value + " (class: " + value.getClass().getSimpleName() + ")";
}
return "null";
}

public static <T> TestLiveDataObserver<T> create() {
return new TestLiveDataObserver<>();
}

public static <T> TestLiveDataObserver<T> create(Observer<T> delegate) {
return new TestLiveDataObserver<>(delegate);
}

public static <T> TestLiveDataObserver<T> test(LiveData<T> liveData) {
TestLiveDataObserver<T> observer = create();
liveData.observe(observer.lifecycleOwner(), observer);
return observer.markState(Lifecycle.State.STARTED);
}

public static <T> TestLiveDataObserver<T> test(LiveData<T> liveData, Observer<T> delegate) {
TestLiveDataObserver<T> observer = create(delegate);
liveData.observe(observer.lifecycleOwner(), observer);
return observer.markState(Lifecycle.State.STARTED);
}

static final class FakeOwner implements LifecycleOwner {
private final LifecycleRegistry fakeRegistry = new LifecycleRegistry(this);

@NonNull
@Override
public Lifecycle getLifecycle() {
return fakeRegistry;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.jraska.livedata.testing;

import org.junit.Test;

import static org.junit.Assert.*;

/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include ':client', ':client-domain', ':client-data'
include ':client', ':client-domain', ':client-data', ':livedata-testing'