Você está na página 1de 21

26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Capítulo 6 : Montando um
Repositório

Neste capítulo, começaremos discutindo a camada de dados do aplicativo e os


componentes que compõem essa camada, incluindo repositórios e fontes de
dados. Em seguida, passaremos ao tópico de repositórios, um dos componen-
tes da camada de aplicação, e o papel que desempenham no gerenciamento
dos dados de uma aplicação. No exercício deste capítulo, continuaremos o pro-
jeto iniciado no capítulo anterior, fornecendo as implementações de repositó-
rio para as abstrações lá definidas e também introduzindo novas abstrações
para os diferentes tipos de fontes de dados.

Neste capítulo, abordaremos os seguintes tópicos:

Criando a camada de dados


Criando repositórios

Ao final do capítulo, você terá aprendido o que é a camada de dados e como


podemos criar repositórios para um aplicativo Android.

Requerimentos técnicos

Os requisitos de hardware e software são os seguintes:

Android Studio Arctic Fox 2020.3.1 Patch 3

Os arquivos de código para este capítulo podem ser encontrados aqui:


https://github.com/PacktPublishing/Clean-Android-
Architecture/tree/main/Chapter6 .

Confira o vídeo a seguir para ver o Código em Ação: https://bit.ly/3NpAhNs

Criando a camada de dados

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 1/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Nesta seção, veremos a camada de dados deum aplicativo Android e os compo-


nentes que normalmente fazem parte da camada de dados.

A camada de dados é a camada na qual os dados são criados e gerenciados.


Isso significa que essa camada é responsável por criar, ler, atualizar e excluir
dados, além de gerenciar e garantir que os dados da internet sejam sincroni-
zados com os dados persistentes.

No capítulo anterior, vimos que os casos de uso dependem de uma abstração


de uma classe de repositório, e pode havervários repositórios para diferentes
tipos de dados. Os repositórios representam o ponto de entrada na camada de
dados e são responsáveis ​por gerenciar várias fontes de dados e centralizar os
dados. As fontes de dados representam o outro componente da camada de da-
dos e são responsáveis ​por gerenciar os dados de uma determinada fonte (in-
ternet, Sala, armazenamento de dados e similares).

Um exemplo de como pode ser a camada de dados de um determinado con-


junto de dados, que usa duas fontes de dados, é mostrado na figura a seguir:

Figura 6.1 – Exemplo de camada de dados

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 2/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

No diagrama anterior, temos um exemplo de como seria uma camada de da-


dos quando conectada à camada de domínio. Podemosobserve que a classe
UseCase depende de uma abstração de Repositório , que representa a camada

de domínio. A camada de dados é representada por RepositoryImpl , que é a


implementação da abstração do Repositório . A classe RepositoryImpl de-
pende das duas implementações de fonte de dados: RemoteDataSourceImpl e
LocalDataSourceImpl . Cada fonte de dados depende então de uma implemen-

tação específica para gerenciar dados da Internet usando Retrofit no caso de


RetrofitService , ou usando uma classe de acesso a dados específica que usa

Room no caso de DbDao .

Essa abordagem apresenta um problema devido à dependência direta entre


RepositoryImpl e RemoteDataSourceImpl , e o problema surge quando podemos

querer trocar Retrofit ou Room por alternativas. Se quisermos trocar essas bi-
bliotecas por outras, corremos o risco de mudanças na classe RepositoryImpl ,
que viola o princípio de responsabilidade única. A solução para isso é como a
solução que tivemos para resolver as dependênciasentre os casos de uso e os
repositórios, e isso é inverter as dependências entre o repositório e as fontes
de dados. Isso se pareceria com o seguinte:

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 3/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Figura 6.2 – Camada de dados com dependências invertidas

No diagrama anterior, introduzimos duas abstrações para cada fonte de da-


dos, denominadas RemoteDataSource e LocalDataSource . RepositoryImpl agora
depende dessas duas abstrações e todas as conversões entre objetos relaciona-
dos a Retrofit ou Room e entidades de domínio agora devem ser colocadas em
RemoteDataSourceImpl ou LocalDataSourceImpl , que herdam as novas abstra-

ções e continuarão a manipular os dados de Retrofit ou Room. Se quisermos


dividir a camada de dados emdiferentes módulos Gradle, teremos o seguinte:

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 4/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Figura 6.3 – Módulos da camada de dados

O diagrama anterior mostra as dependências do módulo Gradle entre o reposi-


tório e as fontes de dados locais e remotas. Aqui podemos ver o benefício da
inversão de dependência, que nos permite ter um módulo de repositório sepa-
rado sem depender de Retrofit ou Room.

Nesta seção, discutimos a camada de dados e os componentes dentro dela e


como gerenciar as dependências entre todos os componentes. Na seção a se-
guir, examinaremos mais detalhadamente os repositórios e como implementá-
los.

Criando repositórios

Nesta seção, veremoso que é um repositório e o papel que ele desempenha na


camada de dados de um aplicativo e como podemos criar repositórios com vá-
rias fontes de dados.

O repositório representa uma abstração dos dados que um aplicativo usa e é


responsável por gerenciar e centralizar os dados de uma ou várias fontes de
dados.

No capítulo anterior, definimos a seguinte entidade:

classe de dados Usuário(


    val id: String,
    val firstName: String,

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 5/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

    val lastName: String,


    val email: String
) {
    fun getFullName() = "$firstName $lastName"
}
Aqui temos uma classe de dados User simples com alguns campos relevantes.
A abstração do repositório para os dados do usuário é a seguinte:

interface UserRepository {
    fun getUser(id: String): Flow<User>
}
Aqui temos uma interface chamada UserRepository que é responsável por bus-
car as informações do usuário em um fluxo Kotlin.

Se quisermos buscar dados da internet, devemos primeiro definir uma abstra-


ção UserRemoteDataSource :

interface UserRemoteDataSource {
    fun getUser(id: String): Flow<User>
}
Neste caso, temos uma interface semelhante a como UserRepository é definido
com um método simples pararecuperar um objeto User . Agora podemos im-
plementar UserRepository para usar esta fonte de dados:

class UserRepositoryImpl(valor privado


userRemoteDataSource:
    UserRemoteDataSource): UserRepository {
   
    override fun getUser(id: String): Flow<User> =
        userRemoteDataSource.getUser(id)
    
}
Aqui temos uma dependência de UserRemoteDataSource e invocamos o método
getUser . Se quisermos persistir os dados do usuário remoto localmente, preci-

saremos definir uma abstração UserLocalDataSource , que será responsável


por inserir o usuário:

interface UserLocalDataSource {
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 6/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

    suspender a diversão insertUser(user: User)


}
Aqui temos um método para inserir um usuário no armazenamento local.
Agora podemos atualizar UserRepositoryImpl para conectar as fontes de dados
e inserir um usuário depois que ele foi recuperado:

class UserRepositoryImpl(
    valor privado userRemoteDataSource:
UserRemoteDataSource,
    valor privado userLocalDataSource:
UserLocalDataSource
): UserRepository {
    override fun getUser(id: String): Flow<User> =
        userRemoteDataSource.getUser(id)
        .em cada {
            userLocalDataSource.insertUser(it)
        }
}
Isso representa um caso de uso simples para fontes de dados, mas podemos
usar repositórios para melhorar a experiência do usuário. Por exemplo, pode-
mos alterar a implementação do repositório para retornar odados salvos e
têm um método separado para buscar os dados remotamente. Podemos apro-
veitar os fluxos, que podem emitir vários usuários em um fluxo:

interface UserLocalDataSource {
    suspender a diversão insertUser(user: User)
    fun getUser(id: String): Flow<User>
}
No exemplo anterior, adicionamos o método getUser para recuperar um ob-
jeto User , que foi persistido localmente. Precisaremos modificar a abstração
do repositório da seguinte forma:

interface UserRepository {
    fun getUser(id: String): Flow<User>
    fun refreshUser(id: String): Flow<User>
}

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 7/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Aqui, adicionamos o método refreshUser , que, quando implementado, será


responsável por buscar um novo usuário na internet. A implementação será a
seguinte:

class UserRepositoryImpl(
    valor privado userRemoteDataSource:
UserRemoteDataSource,
    valor privado userLocalDataSource:
UserLocalDataSource
): UserRepository {
    override fun getUser(id: String): Flow<User> =
        userLocalDataSource.getUser(id)
    override fun refreshUser(id: String): Flow<User> =
        userRemoteDataSource.getUser(id)
        .em cada {
            userLocalDataSource.insertUser(it)
        }
}
Aqui, retornamos o usuário persistente no método getUser e, no método re‐
freshUser , agora buscamos os dados remotos e os inserimos localmente. Se es-

tivermos usando bibliotecas como Room, isso acionará oemissão de um novo


objeto User , que virá de UserLocalDataSource . Isso significa que todos os assi-
nantes do método getUser serão notificados de uma mudança e receberão um
novo objeto User .

Também podemos usar repositórios para armazenar dados em cache na me-


mória. Um exemplo disso seria o seguinte:

class UserRepositoryImpl(
    valor privado userRemoteDataSource:
UserRemoteDataSource,
    valor privado userLocalDataSource:
UserLocalDataSource
): UserRepository {
    valor privado usersFlow = MutableStateFlow
        (emptyMap<String, User>().toMutableMap())
    override fun getUser(id: String): Flow<User> =

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 8/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

        usersFlow.flatMapLatest {
        val usuário = it[id]
        if (usuário != null) {
            flowOf(usuário)
        } senão {
            userLocalDataSource.getUser(id)
                .onEach { persistedUser ->
                    saveUser(usuário persistente)
                }
        }
    }
    override fun refreshUser(id: String): Flow<User> =
        userRemoteDataSource.getUser(id)
        .em cada {
            saveUser(it)
            userLocalDataSource.insertUser(it)
        }
    diversão privada saveUser(user: User) {
        val map = usersFlow.value
        mapa[user.id] = usuário
        usersFlow.value = map
    }
}
Aqui, adicionamos um novo objeto MutableStateFlow , que conterá um mapa
no qual as chaves são representadas pelos IDs dos usuários e os valores são os
usuários. No método getUser , verificamos se o usuário está armazenado na
memória e retornamos o valor da memória se estiver presente, caso contrário,
obtemos os dados persistentes, que depois armazenaremos na memória. No
método refreshUser , persistimos o valor na memória e persistimos os dados
localmente.

Como definimos a abstração do repositório para retornar entidades, devemos


tentar o máximo possível usar entidades no repositório e nas abstrações da
fonte de dados. No entanto, podemos precisar de definições de objeto específi-
cas para lidar com o processamento de dados das fontes de dados. Podemos
definir essas classes específicas nesta camada e depois convertê-las em entida-
des no repositórioimplementação.

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 9/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Nesta seção, vimos como podemos criar repositórios e como eles podem ser
usados ​para gerenciar dados em um aplicativo. Na seção a seguir, veremos um
exercício no qual criaremos repositórios para um aplicativo.

Exercício 06.01 – Criando repositórios

Modifique o Exercício 05.01: Construindo uma camada de domínio , para que


um novo módulo de biblioteca seja criado no Android Studio. O móduloserá
nomeado repositório de dados e terá uma dependência no módulo de domínio
. Neste módulo, implementaremos as classes de repositório do módulo de do-

mínio da seguinte forma:

UserRepositoryImpl terá dependências nas seguintes fontes de dados: User‐

RemoteDataSource , que buscará uma lista e um usuário por ID, e UserLocal‐

DataSource , que terá métodos para inserir uma lista de usuários e obter

uma lista dos mesmos. UserRepositoryImpl sempre carregará os usuários


remotos e os inserirá localmente.
PostRepositoryImpl terá dependências nas seguintes fontes de dados: Pos‐

tRemoteDataSource , que buscará uma lista de usuários e um usuário por ID,

e PostLocalDataSource , que terá métodos para inserir uma lista de posts e


obter uma lista dos mesmos. PostRepositoryImpl sempre carregará os posts
remotos e os inserirá localmente.
InteractionRepositoryImpl terá uma dependência de uma única fonte de

dados, LocalInteractionDataSource , que será responsável por carregar


uma interação e salvá-la. InteractionRepositoryImpl carregará a interação
e salvará uma nova interação.

Para concluir este exercício, você precisará fazer o seguinte:

Crie o módulo de repositório de dados no Android Studio


Crie as fontes de dados e o repositório do usuário
Crie as fontes de dados e o repositório da postagem
Criar a fonte de dados de interação e o repositório

Siga estas etapas para concluiro exercício:

1. Crie um novo módulo chamado data-repository , que será um módulo An-


droid Library.
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 10/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

2. Certifique-se de que no arquivo build.gradle de nível superior , as seguin-


tes dependências estejam definidas:
script de construção {
     …
    dependências {
        classpath gradlePlugins.android
        classpath gradlePlugins.kotlin
        classpath gradlePlugins.hilt
    }
}
3. No arquivo build.gradle do módulo data-repository , certifique-se de que
os seguintes plugins estejam presentes:
plug-ins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}
4. No mesmo arquivo, altereas configurações para as definidas no arquivo
build.gradle de nível superior :
andróide {
    compileSdk defaultCompileSdkVersion
    configuração padrão {
        minSdk defaultMinSdkVersion
        targetSdk defaultTargetSdkVersion
        …
    }
    …
    opções de compilação {
        sourceCompileVersion javaCompileVersion
        targetCompatibility javaCompileVersion
    }
    kotlinOptions {
        jvmTarget = jvmTarget
    }
}

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 11/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

5. No mesmo arquivo, certifique-se de que as seguintes dependências sejam


especificadas:
dependências {
    implementação(projeto(caminho: ":domínio"))
    implementação coroutines.coroutinesAndroid
    implementação di.hiltAndroid
    kapt di.hiltCompiler
    testImplementação test.junit
    testImplementação test.coroutines
    testImplementação test.mockito
}

Aqui, estamos usando o método de implementação para adicionar uma depen-


dência ao módulo :domain , da mesma forma que outras bibliotecas são refe-
renciadas. No Gradle também temos a opção de usar o método api . Isso torna
as dependências de um módulo públicas para outros módulos. Isso, por sua
vez, pode ter efeitos colaterais potenciais, como vazamentodependências que
devem ser mantidas privadas. Neste exemplo, podemos ser mais bem servidos
usando o método api para o módulo :domain por causa do relacionamento pró-
ximo entre os dois módulos (o que faria com que todos os módulos que depen-
dem de :data-repository não precisassem adicionar a dependência a :domain
). No entanto, dependências como Hilt e Coroutines devem ser mantidas com o
método de implementação porque gostaríamos de evitar expor essas bibliote-
cas em módulos que não as utilizam.

6. No módulo de repositório de dados , crie um novo pacote chamado


data_source .

7. No pacote data_source , crie um novo pacote chamado remote .


8. No pacote remoto , crie a interface RemoteUserDataSource :
interface RemoteUserDataSource {
    fun getUsers(): Flow<List<User>>
    fun getUser(id: Long): Flow<User>
}
9. No pacote remoto , crie a interface RemotePostDataSource :
interface RemotePostDataSource {
    fun getPosts(): Flow<List<Post>>
    fun getPost(id: Long): Flow<Post>

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 12/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

}
10. No pacote data_source , crie umnovo pacote chamado local .
11. No pacote local , crie a interface LocalUserDataSource :
interface LocalUserDataSource {
    fun getUsers(): Flow<List<User>>
    suspender diversão addUsers(users: List<User>)
}
12. No pacote local , crie a interface LocalPostDataSource :
interface LocalPostDataSource {
    fun getPosts(): Flow<List<Post>>
    suspender diversão addPosts(posts: List<Post>)
}
13. No pacote local , crie o pacote LocalInteractionDataSource :
interface LocalInteractionDataSource {
    fun getInteraction(): Flow<Interaction>
    suspend fun saveInteraction(interaction:
Interaction)
}
14. Ao lado do pacote data_source , crieum novo pacote chamado repositório .
15. No pacote de repositório , crie a classe UserRepositoryImpl :
class UserRepositoryImpl @Inject construtor(
    valor privado remoteUserDataSource:
        RemoteUserDataSource,
    valor privado localUserDataSource:
        LocalUserDataSource
): UserRepository {
    substituir fun getUsers(): Flow<List<User>> =
        remoteUserDataSource.getUsers()
        .em cada {
            localUserDataSource.addUsers(it)
        }
    override fun getUser(id: Long): Flow<User> =
remoteUserDataSource.getUser(id)
        .em cada {
            localUserDataSource.addUsers(listOf(it))
        }
}

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 13/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Aqui, buscamos o usuáriodados da fonte de dados remota e armazená-los


localmente.

16. No mesmo pacote, crie a classe PostRepositoryImpl :


class PostRepositoryImpl @Inject construtor(
    valor privado remotePostDataSource:
        RemotePostDataSource,
    valor privado localPostDataSource:
        LocalPostDataSource
): : PostRepository {
    substituir fun getPosts(): Flow<List<Post>> =
        remotePostDataSource.getPosts()
        .em cada {
            localPostDataSource.addPosts(it)
        }
    override fun getPost(id: Long): Flow<Post> =
        remotePostDataSource.getPost(id)
        .em cada {
            localPostDataSource.addPosts(listOf(it))
        }
}

Aqui estamos pegandoos dados de postagem da fonte de dados remota e


usando a fonte de dados local para persistir os dados.

17. No mesmo pacote, crie a classe InteractionRepositoryImpl :


class InteractionRepositoryImpl @Inject construtor(
    valor privado interaçãoDataSource:
        LocalInteractionDataSource
): Repositório de Interação {
    substituir fun getInteraction(): Flow<Interaction>
=
        interaçãoDataSource.getInteraction()
    substituir fun saveInteraction(interaction:
       Interação): Fluxo<Interação> = fluxo {
        interaçãoDataSource.saveInteraction(interação)
        this.emit(Unidade)

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 14/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

    }.flatMapLatest {
        getInteração()
    }
}

Aqui, estamos apenas interagindo com a fonte de dados local para ler e arma-
zenar os dados.

18. Agora queremos usar Hilt paravincule a abstração do repositório com a im-
plementação, então precisaremos criar um pacote chamado injeção pró-
ximo aos pacotes data_source e repositório .
19. Dentro do pacote de injeção , crie uma classe chamada RepositoryModule :
@Módulo
@InstallIn(SingletonComponent::class)
classe abstrata RepositoryModule {
    @Binds
    diversão abstrata
bindPostRepository(postRepositoryImpl
        : PostRepositoryImpl): PostRepository
    @Binds
    diversão abstrata bindUserRepository
        (userRepositoryImpl: UserRepositoryImpl):
            UserRepository
    @Binds
    diversão abstrata bindInteractionRepository
        (interactionRepositoryImpl:
            InteractionRepositoryImpl):
                InteractionRepository
}

Aqui, estamos usando a anotação @Binds Hilt, que mapeia a implementação de


um repositório anotado com @Inject com a abstração.

20. Para testar a unidade do código, agora precisaremos criar uma nova pasta
chamada resources no testepasta do módulo de repositório de dados .
21. Dentro da pasta de recursos, crie uma pasta chamada mockito-extensions
e, dentro dessa pasta, crie um arquivo chamado

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 15/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

org.mockito.plugins.MockMaker e, dentro desse arquivo, adicione o seguinte

texto: mock-maker-inline .
22. Crie uma classe UserRepositoryImplTest para teste de unidade dos métodos
UserRepositoryImpl :
class UserRepositoryImplTest {
    valor privado remoteUserDataSource =
        mock<RemoteUserDataSource>()
    valor privado localUserDataSource =
        mock<LocalUserDataSource>()
    private val repositoryImpl = UserRepositoryImpl
        (remoteUserDataSource, localUserDataSource)
   
}
23. Na classe UserRepositoryImplTest , adicione um método de teste para cada
repositóriométodo:
class UserRepositoryImplTest {
     …
    @ExperimentalCoroutinesApi
    @Teste
    fun testGetUsers() = runBlockingTest {
        val usuários = listOf(Usuário(1, "nome", "nome
de usuário",
            "o email"))
        sempre(remoteUserDataSource.getUsers()).
            thenReturn(flowOf(usuários))
        val resultado =
repositoryImpl.getUsers().first()
        assertEquals(usuários, resultado)
        verifique(localUserDataSource).addUsers(usuári
os)
    }
    @ExperimentalCoroutinesApi
    @Teste
    fun testGetUser() = runBlockingTest {
        val id = 1L
        val user = User(id, "nome", "nome de usuário",
            "o email"

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 16/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

)
        sempre(remoteUserDataSource.getUser(id))
            .thenReturn(flowOf(usuário))
        val resultado = repositórioImpl.getUser(id).
            primeiro()
        assertEquals(usuário, resultado)
        verifique(localUserDataSource).addUsers(listOf
(usuário))
    }
}

Nesta classe, testamos cada um dos métodos da classe UserRepositoryImpl


zombando do localdados e fontes de dados remotas e verificando se os dados
obtidos da fonte de dados remota são inseridos na fonte de dados local.

24. Crie uma classe PostRepositoryImplTest para testar a classe


PostRepositoryImpl :
class PostRepositoryImplTest {
    valor privado remotePostDataSource =
        mock<RemotePostDataSource>()
    valor privado localPostDataSource =
        mock<LocalPostDataSource>()
    private val repositoryImpl = PostRepositoryImpl
        (remotoPostDataSource, localPostDataSource)
}
25. Crie testes de unidade para cada um dosmétodos na classe
PostRepositoryImpl :
class PostRepositoryImplTest {
    …
    @ExperimentalCoroutinesApi
    @Teste
    fun testGetPosts() = runBlockingTest {
        val posts = listOf(Post(1, 1, "título",
            "corpo"))
        sempre(remotePostDataSource.getPosts())
            .thenReturn(flowOf(posts))

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 17/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

        val resultado =
repositórioImpl.getPosts().first()
        Assert.assertEquals(postagens, resultado)
        verifique(localPostDataSource).addPosts(posts)
    }
    @ExperimentalCoroutinesApi
    @Teste
    fun testGetPost() = runBlockingTest {
        val id = 1L
        val post = Post(id, 1, "título", "corpo")
        sempre(remotePostDataSource.getPost(id)).thenR
eturn(flowOf(post))
        val resultado =
            repositórioImpl.getPost(id).first()
        Assert.assertEquals(postagem, resultado)
        verifique(localPostDataSource).addPosts(listOf
(post))
    }
}

Nesta aula, realizamos os mesmos testesque fizemos para UserRepositoryImpl .

26. Crie uma classe InteractionRepositoryImplTest para testar a classe


InteractionRepositoryImpl :
class InteractionRepositoryImplTest {
    valor privado localInteractionDataSource =
        mock<LocalInteractionDataSource>()
    private val repositoryImpl =
        InteractionRepositoryImpl
        (localInteractionDataSource)
}
27. Crie testes de unidade para cada um dos métodos na classe
InteractionRepositoryImpl :
class InteractionRepositoryImplTest {
    …
    @ExperimentalCoroutinesApi
    @Teste

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 18/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

    fun testGetInteraction() = runBlockingTest {


        val interação = Interação(10)
        sempre(localInteractionDataSource.
            getInteração()).
                thenReturn(flowOf(interação))
        val resultado =
repositórioImpl.getInteraction()
            .primeiro()
        assertEquals(interação, resultado)
    }
    @ExperimentalCoroutinesApi
    @Teste
    fun testSaveInteraction() = runBlockingTest {
        val interação = Interação(10)
        sempre(localInteractionDataSource.
            getInteraction()).thenReturn
                (flowOf(interação))
        val resultado =
repositórioImpl.saveInteraction
            (interação).primeiro()
        veriy(localInteractionDataSource).
             salvarInteração(interação)
        assertEquals(interação, resultado)
    }
}

Nesta aula, zombamos do localfonte de dados e, em seguida, verificamos se o


repositório tem as chamadas apropriadas no mock LocalInteractionDataStore
.

Se executarmos os testes, devemos ver algo como a captura de tela a seguir:

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 19/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Figura 6.4 – Saída dos testes unitários do repositório

Neste exercício, criamos um novo módulo no qual implementamos nossos re-


positórios e definimos novas abstrações para as fontes de dados que os reposi-
tórios usarão. Aqui, demos continuidade aointegração com outras bibliotecas,
como Hilt para injeção de dependência e fluxos Kotlin para manipular os da-
dos em uma abordagem reativa. O uso de injeção de dependência tornou os
testes de unidade simples de escrever porque poderíamos facilmente fornecer
mocks.

Resumo

Neste capítulo, começamos a examinar a camada de dados de um aplicativo


Android e fornecemos uma visão geral dos componentes que fazem parte
dessa camada. Também analisamos o componente Repositório, que é respon-
sável por gerenciar os dados fornecidos por uma ou mais fontes de dados, e
fornecemos exemplos de como podemos construir diferentes repositórios.
Também analisamos a relação entre repositórios e fontes de dados e como po-
demos desacoplar ainda mais os componentes com inversão de dependência,
para manter nossos repositórios não afetados por alterações nas bibliotecas
usadas para buscar dados. Por fim, analisamos um exercício sobre como pode-
mos construir repositórios com fontes de dados locais e remotas. No capítulo
seguinte, continuaremos com a camada de dados e como podemos integrar as
fontes de dados remotas e locais com bibliotecas como Room e Retrofit.

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 20/21
26/10/2022 21:44 Chapter 6: Assembling a Repository | Clean Android Architecture

Apoiar Sair

© 2022 O'REILLY MEDIA, INC.  TERMOS DE SERVIÇO POLÍTICA DE PRIVACIDADE

https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_06_ePub.xhtml#_idParaDest-77 21/21

Você também pode gostar