Skip to content

Instantly share code, notes, and snippets.

@rohans-doordash
Created June 27, 2017 01:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rohans-doordash/de1b669075d6cdd46fe68890a9948244 to your computer and use it in GitHub Desktop.
Save rohans-doordash/de1b669075d6cdd46fe68890a9948244 to your computer and use it in GitHub Desktop.
Login MVP
package com.doordash.mvpexample.data;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import com.doordash.mvpexample.api.AuthenticationApi;
import com.doordash.mvpexample.helpers.SharedPreferencesHelper;
import com.doordash.mvpexample.models.Token;
import javax.inject.Inject;
import io.reactivex.Single;
public class AuthenticationManager {
@VisibleForTesting
static final String AUTH_TOKEN_SHARED_PREFS_KEY = "auth-token";
private SharedPreferencesHelper sharedPreferencesHelper;
private AuthenticationApi authenticationApi;
@Inject
public AuthenticationManager(AuthenticationApi authenticationApi,
SharedPreferencesHelper sharedPreferencesHelper) {
this.sharedPreferencesHelper = sharedPreferencesHelper;
this.authenticationApi = authenticationApi;
}
public Single<Token> getAuthToken(@NonNull String email, @NonNull String password) {
return Single.fromCallable(() -> {
String sharedPreferencesString = sharedPreferencesHelper.getString(AUTH_TOKEN_SHARED_PREFS_KEY);
if (sharedPreferencesString != null) {
return Single.just(new Token(sharedPreferencesString));
}
return authenticationApi.getTokenForCredentials(email, password).map(token -> {
sharedPreferencesHelper.save(AUTH_TOKEN_SHARED_PREFS_KEY, token.getToken());
return token;
});
}).flatMap(tokenSingle -> tokenSingle);
}
}
package com.doordash.mvpexample.data;
import com.doordash.mvpexample.api.AuthenticationApi;
import com.doordash.mvpexample.helpers.SharedPreferencesHelper;
import com.doordash.mvpexample.models.Token;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import io.reactivex.Single;
import static com.doordash.mvpexample.data.AuthenticationManager.AUTH_TOKEN_SHARED_PREFS_KEY;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class AuthenticationManagerTest {
private static final String EMAIL = "user@website.com";
private static final String PASSWORD = "password";
private static final String API_TOKEN = "api-token";
private static final String CACHED_TOKEN = "cached-token";
@Mock
AuthenticationApi authenticationApi;
@Mock
SharedPreferencesHelper sharedPreferencesHelper;
private AuthenticationManager authenticationManager;
@Before
public void setUp() throws Exception {
// instantiate manager with mock dependencies
MockitoAnnotations.initMocks(this);
authenticationManager = new AuthenticationManager(authenticationApi, sharedPreferencesHelper);
}
@Test
public void getAuthToken_whenNotCached_callApiAndCache() throws Exception {
when(sharedPreferencesHelper.getString(AUTH_TOKEN_SHARED_PREFS_KEY))
.thenReturn(null); // cache is empty
when(authenticationApi.getTokenForCredentials(EMAIL, PASSWORD))
.thenReturn(Single.just(new Token(API_TOKEN))); // api will succeed
Token token = authenticationManager.getAuthToken(EMAIL, PASSWORD).blockingGet();
assertEquals(API_TOKEN, token.getToken());
verify(sharedPreferencesHelper).save(AUTH_TOKEN_SHARED_PREFS_KEY, API_TOKEN);
}
@Test
public void getAuthToken_whenCached_getCachedValue() throws Exception {
when(sharedPreferencesHelper.getString(AUTH_TOKEN_SHARED_PREFS_KEY))
.thenReturn(CACHED_TOKEN); // cache is filled
Token token = authenticationManager.getAuthToken(EMAIL, PASSWORD).blockingGet();
assertEquals(CACHED_TOKEN, token.getToken());
}
}
package com.doordash.mvpexample.ui.login;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatEditText;
import android.widget.Toast;
import com.doordash.mvpexample.R;
import com.doordash.mvpexample.application.MvpApplication;
import com.doordash.mvpexample.ui.MainActivity;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class LoginActivity extends AppCompatActivity implements LoginContract.View {
@Inject
LoginContract.Presenter presenter;
@BindView(R.id.login_email)
AppCompatEditText emailEditText;
@BindView(R.id.login_password)
AppCompatEditText passwordEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
MvpApplication.getAppComponent().inject(this);
ButterKnife.bind(this);
presenter.setView(this);
}
@Override
protected void onPause() {
presenter.onPause();
super.onPause();
}
@NonNull
@Override
public String getEmailInput() {
return emailEditText.getText().toString();
}
@NonNull
@Override
public String getPasswordInput() {
return passwordEditText.getText().toString();
}
@Override
public void goToMainActivity() {
startActivity(MainActivity.makeIntent(this));
}
@Override
public void showError(@NonNull String error) {
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
}
@OnClick(R.id.login_button)
public void onClickLoginButton() {
presenter.onClickLoginButton();
}
}
package com.doordash.mvpexample.ui.login;
import android.support.annotation.NonNull;
import com.doordash.mvpexample.ui.BaseContract;
public class LoginContract {
public interface View {
void goToMainActivity();
void showError(@NonNull String error);
@NonNull
String getEmailInput();
@NonNull
String getPasswordInput();
}
public interface Presenter {
/**
* Late initializer for {@link View} interface
*/
void setView(View view);
void onPause();
void onClickLoginButton();
}
}
package com.doordash.mvpexample.ui.login;
import com.doordash.mvpexample.data.AuthenticationDataSource;
import com.doordash.mvpexample.helpers.rx.RxSchedulerProvider;
import com.doordash.mvpexample.models.Token;
import com.doordash.mvpexample.ui.BaseRxPresenter;
import io.reactivex.SingleObserver;
import io.reactivex.disposables.Disposable;
public class LoginPresenter implements LoginContract.Presenter {
private AuthenticationManager authenticationManager;
public LoginPresenter(RxSchedulerProvider rxSchedulerProvider,
AuthenticationManager authenticationManager) {
super(rxSchedulerProvider);
this.authenticationManager = authenticationManager;
}
public void onClickLoginButton() {
authenticationManager.getAuthToken(view.getEmailInput(), view.getPasswordInput())
.subscribe(new SingleObserver<Token>() {
@Override
public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
addDisposable(d);
}
@Override
public void onSuccess(@io.reactivex.annotations.NonNull Token token) {
view.goToMainActivity();
}
@Override
public void onError(@io.reactivex.annotations.NonNull Throwable e) {
view.showError(e.getLocalizedMessage());
}
});
}
}
package com.doordash.mvpexample.ui.login;
import com.doordash.mvpexample.data.AuthenticationDataSource;
import com.doordash.mvpexample.helpers.rx.ImmediateRxSchedulerProvider;
import com.doordash.mvpexample.models.Token;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import io.reactivex.Single;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class LoginPresenterTest {
private static final String EMAIL = "user@website.com";
private static final String PASSWORD = "password";
private static final String TOKEN = "token";
@Mock
AuthenticationDataSource authenticationDataSource;
@Mock
LoginContract.View loginView;
LoginPresenter presenter;
@Before
public void setUp() throws Exception {
// instantiate presenter with mock dependencies
// pass in ImmediateRxSchedulerProvider to make rx calls blocking
MockitoAnnotations.initMocks(this);
presenter = new LoginPresenter(new ImmediateRxSchedulerProvider(), authenticationDataSource);
presenter.setView(loginView);
// mock view getters
when(loginView.getEmailInput()).thenReturn(EMAIL);
when(loginView.getPasswordInput()).thenReturn(PASSWORD);
}
@Test
public void onClickLoginButton_whenLoginSucceeds_goToMainActivity() throws Exception {
when(authenticationDataSource.getAuthToken(EMAIL, PASSWORD))
.thenReturn(Single.just(new Token(TOKEN))); // fake successful login
presenter.onClickLoginButton();
verify(loginView).goToMainActivity();
}
@Test
public void onClickLoginButton_whenLoginFails_showError() throws Exception {
Exception error = new Exception("error message");
when(authenticationDataSource.getAuthToken(EMAIL, PASSWORD))
.thenReturn(Single.error(error)); // fake failed login
presenter.onClickLoginButton();
verify(loginView).showError(error.getLocalizedMessage());
}
}
package com.doordash.mvpexample.models;
import android.support.annotation.NonNull;
public class Token {
private String token;
public Token(@NonNull String token) {
this.token = token;
}
public String getToken() {
return token;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment