Android Déplacement de la logique métier hors des composants Android


Exemple

Une grande partie de la valeur des tests unitaires JVM locaux provient de la façon dont vous concevez votre application. Vous devez le concevoir de telle sorte que vous puissiez découpler votre logique métier de vos composants Android. Voici un exemple d'utilisation du modèle Model-View-Presenter . Permet de s'exercer en mettant en place un écran d'inscription de base qui ne prend qu'un nom d'utilisateur et un mot de passe. Notre application Android est chargée de valider que le nom d'utilisateur fourni par l'utilisateur n'est pas vide et que le mot de passe comporte au moins huit caractères et au moins un chiffre. Si le nom d'utilisateur / mot de passe est valide, nous effectuons notre appel api d'inscription, sinon nous affichons un message d'erreur.

Exemple où la logique métier est fortement couplée au composant Android.

public class LoginActivity extends Activity{
    ...
    private void onSubmitButtonClicked(){
        String username = findViewById(R.id.username).getText().toString();
        String password = findViewById(R.id.password).getText().toString();
        boolean isUsernameValid = username != null && username.trim().length() != 0;
        boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
        if(isUsernameValid && isPasswordValid){
            performSignUpApiCall(username, password);
        } else {
            displayInvalidCredentialsErrorMessage();
        }
    }
}

Exemple où la logique métier est découplée du composant Android.

Nous définissons ici dans une seule classe, LoginContract, les différentes interactions entre nos différentes classes.

public interface LoginContract {
    public interface View {
        performSignUpApiCall(String username, String password);
        displayInvalidCredentialsErrorMessage();
    }
    public interface Presenter {
        void validateUserCredentials(String username, String password);
    }
}

Notre LoginActivity est pour la plupart identique, sauf que nous avons supprimé la responsabilité de savoir comment valider le formulaire d'inscription d'un utilisateur (notre logique métier). LoginActivity s'appuiera désormais sur notre nouveau LoginPresenter pour effectuer la validation.

public class LoginActivity extends Activity implements LoginContract.View{
    private LoginContract.Presenter presenter;

    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            presenter = new LoginPresenter(this);
            ....
        }
        ...

        private void onSubmitButtonClicked(){
            String username = findViewById(R.id.username).getText().toString();
            String password = findViewById(R.id.password).getText().toString();
            presenter.validateUserCredentials(username, password);
    }
    ...
}

Votre logique métier réside désormais dans votre nouvelle classe LoginPresenter.

public class LoginPresenter implements LoginContract.Presenter{
    private LoginContract.View view;

    public LoginPresenter(LoginContract.View view){
        this.view = view;
    }

    public void validateUserCredentials(String username, String password){
        boolean isUsernameValid = username != null && username.trim().length() != 0;
        boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
        if(isUsernameValid && isPasswordValid){
            view.performSignUpApiCall(username, password);
        } else {
            view.displayInvalidCredentialsErrorMessage();
        }
    }
}

Et maintenant, nous pouvons créer des tests unitaires JVM locaux par rapport à votre nouvelle classe LoginPresenter.

public class LoginPresenterTest {

    @Mock
    LoginContract.View view;

    private LoginPresenter presenter;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        presenter = new LoginPresenter(view);
    }

    @Test
    public void test_validateUserCredentials_userDidNotEnterUsername_displayErrorMessage() throws Exception {
        String username = "";
        String password = "kingslayer1";
        presenter.validateUserCredentials(username, password);
        Mockito.verify(view). displayInvalidCredentialsErrorMessage();
    }

    @Test
    public void test_validateUserCredentials_userEnteredFourLettersAndOneDigitPassword_displayErrorMessage() throws Exception {
        String username = "Jaime Lanninster";
        String password = "king1";
        presenter.validateUserCredentials(username, password);
        Mockito.verify(view). displayInvalidCredentialsErrorMessage();
    }

    @Test
    public void test_validateUserCredentials_userEnteredNineLettersButNoDigitsPassword_displayErrorMessage() throws Exception {
        String username = "Jaime Lanninster";
        String password = "kingslayer";
        presenter.validateUserCredentials(username, password);
        Mockito.verify(view). displayInvalidCredentialsErrorMessage();
    }

    @Test
    public void test_validateUserCredentials_userEnteredNineLettersButOneDigitPassword_performApiCallToSignUpUser() throws Exception {
        String username = "Jaime Lanninster";
        String password = "kingslayer1";
        presenter.validateUserCredentials(username, password);
        Mockito.verify(view).performSignUpApiCall(username, password);
    }
}

Comme vous pouvez le constater, lorsque nous avons extrait notre logique métier de LoginActivity et l’avons placée dans le POJO LoginPresenter. Nous pouvons maintenant créer des tests unitaires JVM locaux en fonction de notre logique métier.

Il convient de noter que notre changement d’architecture a plusieurs autres implications, comme l’adhésion à chaque classe ayant une responsabilité unique, des classes supplémentaires, etc. Ce ne sont que des effets secondaires de la manière dont je choisis découplage via le style MVP. MVP n’est qu’une façon d’y parvenir, mais vous pouvez également envisager d’autres solutions, telles que MVVM . Il vous suffit de choisir le meilleur système qui fonctionne pour vous.