Fix tests and generify use case parameters.

This commit is contained in:
Fernando Cejas
2016-12-26 16:21:57 -03:00
parent b64a353acb
commit 8c78f8f148
13 changed files with 64 additions and 184 deletions

View File

@@ -24,9 +24,7 @@ import io.reactivex.Observable;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -48,14 +46,9 @@ public class UserDataRepositoryTest {
@Mock private UserEntity mockUserEntity;
@Mock private User mockUser;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
userDataRepository = new UserDataRepository(mockUserDataStoreFactory,
mockUserEntityDataMapper);
userDataRepository = new UserDataRepository(mockUserDataStoreFactory, mockUserEntityDataMapper);
given(mockUserDataStoreFactory.create(anyInt())).willReturn(mockUserDataStore);
given(mockUserDataStoreFactory.createCloudDataStore()).willReturn(mockUserDataStore);
}

View File

@@ -19,8 +19,7 @@ import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.arrow.annotations.VisibleForTesting;
import com.fernandocejas.arrow.optional.Optional;
import com.fernandocejas.arrow.checks.Preconditions;
import io.reactivex.Observable;
import javax.inject.Inject;
@@ -28,12 +27,7 @@ import javax.inject.Inject;
* This class is an implementation of {@link UseCase} that represents a use case for
* retrieving data related to an specific {@link User}.
*/
public class GetUserDetails extends UseCase<User> {
public static final String PARAM_USER_ID_KEY = "userId";
@VisibleForTesting
static final int PARAM_USER_ID_DEFAULT_VALUE = -1;
public class GetUserDetails extends UseCase<User, GetUserDetails.Params> {
private final UserRepository userRepository;
@@ -44,12 +38,21 @@ public class GetUserDetails extends UseCase<User> {
this.userRepository = userRepository;
}
@Override protected Observable<User> buildUseCaseObservable(Optional<Params> params) {
if (params.isPresent()) {
final int userId = params.get().getInt(PARAM_USER_ID_KEY, PARAM_USER_ID_DEFAULT_VALUE);
return this.userRepository.user(userId);
} else {
return Observable.empty();
@Override Observable<User> buildUseCaseObservable(Params params) {
Preconditions.checkNotNull(params);
return this.userRepository.user(params.userId);
}
public static final class Params {
private final int userId;
private Params(int userId) {
this.userId = userId;
}
public static Params forUser(int userId) {
return new Params(userId);
}
}
}

View File

@@ -19,7 +19,6 @@ import com.fernandocejas.android10.sample.domain.User;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Observable;
import java.util.List;
import javax.inject.Inject;
@@ -28,7 +27,7 @@ import javax.inject.Inject;
* This class is an implementation of {@link UseCase} that represents a use case for
* retrieving a collection of all {@link User}.
*/
public class GetUserList extends UseCase<List<User>> {
public class GetUserList extends UseCase<List<User>, Void> {
private final UserRepository userRepository;
@@ -39,7 +38,7 @@ public class GetUserList extends UseCase<List<User>> {
this.userRepository = userRepository;
}
@Override public Observable<List<User>> buildUseCaseObservable(Optional<Params> params) {
@Override Observable<List<User>> buildUseCaseObservable(Void unused) {
return this.userRepository.users();
}
}

View File

@@ -1,50 +0,0 @@
/**
* Copyright (C) 2016 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import java.util.HashMap;
import java.util.Map;
/**
* Class backed by a Map, used to pass parameters to {@link UseCase} instances.
*/
public final class Params {
public static final Params EMPTY = Params.create();
private final Map<String, Object> parameters = new HashMap<>();
private Params() {}
public static Params create() {
return new Params();
}
public void putInt(String key, int value) {
parameters.put(key, value);
}
int getInt(String key, int defaultValue) {
final Object object = parameters.get(key);
if (object == null) {
return defaultValue;
}
try {
return (int) object;
} catch (ClassCastException e) {
return defaultValue;
}
}
}

View File

@@ -18,7 +18,6 @@ package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.arrow.checks.Preconditions;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
@@ -33,7 +32,7 @@ import io.reactivex.schedulers.Schedulers;
* By convention each UseCase implementation will return the result using a {@link DisposableObserver}
* that will execute its job in a background thread and will post the result in the UI thread.
*/
public abstract class UseCase<T> {
public abstract class UseCase<T, Params> {
private final ThreadExecutor threadExecutor;
private final PostExecutionThread postExecutionThread;
@@ -48,17 +47,18 @@ public abstract class UseCase<T> {
/**
* Builds an {@link Observable} which will be used when executing the current {@link UseCase}.
*/
abstract Observable<T> buildUseCaseObservable(Optional<Params> params);
abstract Observable<T> buildUseCaseObservable(Params params);
/**
* Executes the current use case.
*
* @param observer {@link DisposableObserver} which will be listening to the observable build
* by {@link #buildUseCaseObservable(Optional)} ()} method.
* @param params Parameters used to build execute this use case.
* by {@link #buildUseCaseObservable(Params)} ()} method.
* @param params Parameters (Optional) used to build/execute this use case.
*/
public void execute(DisposableObserver<T> observer, Params params) {
final Observable<T> observable = this.buildUseCaseObservable(Optional.of(params))
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));

View File

@@ -17,16 +17,16 @@ package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Observable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -42,6 +42,8 @@ public class GetUserDetailsTest {
@Mock private ThreadExecutor mockThreadExecutor;
@Mock private PostExecutionThread mockPostExecutionThread;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
getUserDetails = new GetUserDetails(mockUserRepository, mockThreadExecutor,
@@ -50,10 +52,7 @@ public class GetUserDetailsTest {
@Test
public void testGetUserDetailsUseCaseObservableHappyCase() {
final Params params = Params.create();
params.putInt(GetUserDetails.PARAM_USER_ID_KEY, USER_ID);
getUserDetails.buildUseCaseObservable(Optional.of(params));
getUserDetails.buildUseCaseObservable(Params.forUser(USER_ID));
verify(mockUserRepository).user(USER_ID);
verifyNoMoreInteractions(mockUserRepository);
@@ -62,22 +61,8 @@ public class GetUserDetailsTest {
}
@Test
public void testShouldReturnEmptyObservableWhenNoParameters() {
final Observable observable = getUserDetails.buildUseCaseObservable(Optional.<Params>absent());
assertThat(observable).isEqualTo(Observable.empty());
verifyZeroInteractions(mockUserRepository);
verifyZeroInteractions(mockPostExecutionThread);
verifyZeroInteractions(mockThreadExecutor);
}
@Test
public void testShouldUseDefaultUserIdValueWhenNoUserIdParameter() {
getUserDetails.buildUseCaseObservable(Optional.of(Params.create()));
verify(mockUserRepository).user(GetUserDetails.PARAM_USER_ID_DEFAULT_VALUE);
verifyNoMoreInteractions(mockUserRepository);
verifyZeroInteractions(mockPostExecutionThread);
verifyZeroInteractions(mockThreadExecutor);
public void testShouldFailWhenNoOrEmptyParameters() {
expectedException.expect(NullPointerException.class);
getUserDetails.buildUseCaseObservable(null);
}
}

View File

@@ -18,14 +18,12 @@ package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.android10.sample.domain.repository.UserRepository;
import com.fernandocejas.arrow.optional.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -47,22 +45,11 @@ public class GetUserListTest {
@Test
public void testGetUserListUseCaseObservableHappyCase() {
getUserList.buildUseCaseObservable(Optional.of(Params.EMPTY));
getUserList.buildUseCaseObservable(null);
verify(mockUserRepository).users();
verifyNoMoreInteractions(mockUserRepository);
verifyZeroInteractions(mockThreadExecutor);
verifyZeroInteractions(mockPostExecutionThread);
}
@Test
@SuppressWarnings("unchecked")
public void testThereShouldNotBeAnyInteractionWithParams() {
Optional params = mock(Optional.class);
getUserList.buildUseCaseObservable(params);
verify(mockUserRepository).users();
verifyZeroInteractions(params);
}
}

View File

@@ -1,49 +0,0 @@
/**
* Copyright (C) 2016 Fernando Cejas Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fernandocejas.android10.sample.domain.interactor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(MockitoJUnitRunner.class)
public class ParamsTest {
private Params params;
@Before
public void setUp() {
params = Params.create();
}
@Test
public void testShouldReturnIntValue() {
params.putInt("key01", 3);
assertThat(params.getInt("key01", 5)).isEqualTo(3);
}
@Test
public void testShouldReturnIntDefaultValue() {
params.putInt("key01", 3);
params.putInt("key02", 4);
assertThat(params.getInt("key03", 5)).isEqualTo(5);
}
}

View File

@@ -17,12 +17,13 @@ package com.fernandocejas.android10.sample.domain.interactor;
import com.fernandocejas.android10.sample.domain.executor.PostExecutionThread;
import com.fernandocejas.android10.sample.domain.executor.ThreadExecutor;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Observable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.TestScheduler;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -35,11 +36,13 @@ public class UseCaseTest {
private UseCaseTestClass useCase;
private TestDisposableObserver testObserver;
private TestDisposableObserver<Object> testObserver;
@Mock private ThreadExecutor mockThreadExecutor;
@Mock private PostExecutionThread mockPostExecutionThread;
@Rule public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() {
this.useCase = new UseCaseTestClass(mockThreadExecutor, mockPostExecutionThread);
@@ -48,7 +51,6 @@ public class UseCaseTest {
}
@Test
@SuppressWarnings("unchecked")
public void testBuildUseCaseObservableReturnCorrectResult() {
useCase.execute(testObserver, Params.EMPTY);
@@ -63,18 +65,25 @@ public class UseCaseTest {
assertThat(testObserver.isDisposed()).isTrue();
}
private static class UseCaseTestClass extends UseCase<Object> {
@Test
public void testShouldFailWhenExecuteWithNullObserver() {
expectedException.expect(NullPointerException.class);
useCase.execute(null, Params.EMPTY);
}
private static class UseCaseTestClass extends UseCase<Object, Params> {
UseCaseTestClass(ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
super(threadExecutor, postExecutionThread);
}
@Override protected Observable buildUseCaseObservable(Optional<Params> params) {
@Override Observable<Object> buildUseCaseObservable(Params params) {
return Observable.empty();
}
@Override public void execute(DisposableObserver observer, Params params) {
super.execute(observer, Params.EMPTY);
@Override
public void execute(DisposableObserver<Object> observer, Params params) {
super.execute(observer, params);
}
}
@@ -93,4 +102,9 @@ public class UseCaseTest {
// no-op by default.
}
}
private static class Params {
private static Params EMPTY = new Params();
private Params() {}
}
}

View File

@@ -17,6 +17,7 @@ package com.fernandocejas.android10.sample.test.presenter;
import android.content.Context;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.presenter.UserDetailsPresenter;
import com.fernandocejas.android10.sample.presentation.view.UserDetailsView;
@@ -27,7 +28,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.fernandocejas.android10.sample.domain.interactor.Params;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
@@ -51,6 +51,7 @@ public class UserDetailsPresenterTest {
}
@Test
@SuppressWarnings("unchecked")
public void testUserDetailsPresenterInitialize() {
given(mockUserDetailsView.context()).willReturn(mockContext);

View File

@@ -17,7 +17,6 @@ package com.fernandocejas.android10.sample.test.presenter;
import android.content.Context;
import com.fernandocejas.android10.sample.domain.interactor.GetUserList;
import com.fernandocejas.android10.sample.domain.interactor.Params;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
import com.fernandocejas.android10.sample.presentation.presenter.UserListPresenter;
import com.fernandocejas.android10.sample.presentation.view.UserListView;
@@ -49,6 +48,7 @@ public class UserListPresenterTest {
}
@Test
@SuppressWarnings("unchecked")
public void testUserListPresenterInitialize() {
given(mockUserListView.context()).willReturn(mockContext);
@@ -56,6 +56,6 @@ public class UserListPresenterTest {
verify(mockUserListView).hideRetry();
verify(mockUserListView).showLoading();
verify(mockGetUserList).execute(any(DisposableObserver.class), any(Params.class));
verify(mockGetUserList).execute(any(DisposableObserver.class), any(Void.class));
}
}

View File

@@ -21,7 +21,7 @@ import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails;
import com.fernandocejas.android10.sample.domain.interactor.Params;
import com.fernandocejas.android10.sample.domain.interactor.GetUserDetails.Params;
import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
@@ -72,9 +72,7 @@ public class UserDetailsPresenter implements Presenter {
}
private void getUserDetails(int userId) {
final Params params = Params.create();
params.putInt(GetUserDetails.PARAM_USER_ID_KEY, userId);
this.getUserDetailsUseCase.execute(new UserDetailsObserver(), params);
this.getUserDetailsUseCase.execute(new UserDetailsObserver(), Params.forUser(userId));
}
private void showViewLoading() {

View File

@@ -21,7 +21,6 @@ import com.fernandocejas.android10.sample.domain.exception.DefaultErrorBundle;
import com.fernandocejas.android10.sample.domain.exception.ErrorBundle;
import com.fernandocejas.android10.sample.domain.interactor.DefaultObserver;
import com.fernandocejas.android10.sample.domain.interactor.GetUserList;
import com.fernandocejas.android10.sample.domain.interactor.Params;
import com.fernandocejas.android10.sample.presentation.exception.ErrorMessageFactory;
import com.fernandocejas.android10.sample.presentation.internal.di.PerActivity;
import com.fernandocejas.android10.sample.presentation.mapper.UserModelDataMapper;
@@ -112,7 +111,7 @@ public class UserListPresenter implements Presenter {
}
private void getUserList() {
this.getUserListUseCase.execute(new UserListObserver(), Params.EMPTY);
this.getUserListUseCase.execute(new UserListObserver(), null);
}
private final class UserListObserver extends DefaultObserver<List<User>> {