Escolar Documentos
Profissional Documentos
Cultura Documentos
Requerimentos técnicos
Inspecionando dependências do
módulo
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 1/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
Na figura anterior, podemos ver que o módulo :domain , que faz parte da ca-
mada de domínio, está no centro, com os módulos das outras camadas tendo
uma dependência em relação a ele. O módulo :app é responsável por montar
todas as dependências, e isso significa que ele terá uma dependência de todos
os outros módulos. Isso significaque estamos em uma boa posição de arquite-
tura limpa porque queremos que as entidades e os casos de uso tenham de-
pendências mínimas de outros componentes. Se continuarmos analisando os
arquivos build.gradle para cada módulo e incluirmos as dependências exter-
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 2/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 3/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
que eles foram desenvolvidos como uma extensão do framework Kotlin. Eles
ainda podem representar um problema se quisermos adaptar nossos casos de
uso para várias plataformas usando o Kotlin Multiplatform. Uma solução para
isso seria desenvolver um plugin reativo que abstraísse o uso de fluxos e
usasse essa abstração nos diferentes módulos. Isso nos permitiria trocar dife-
rentes bibliotecas reativas sem alterar o código dentro do módulo. Embora
esta solução resolva o problema,
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 4/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
UserRepository e InteractionRepository .
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 5/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
}
4. Exclua o uso de @Inject de GetPostUseCase :
class GetPostUseCase(
configuração: configuração,
valor privado postRepository: PostRepository
): UseCase<GetPostUseCase.Request, GetPostUseCase.
Resposta>(configuração) {
…
}
5. Exclua o uso de @Inject de GetUserUseCase :
class GetUserUseCase(
configuração: configuração,
valor privado userRepository: UserRepository
): UseCase<GetUserUseCase.Request, GetUserUseCase.
Resposta>(configuração) {
…
}
6. Excluiro uso de @Inject de UpdateInteractionUseCase :
class UpdateInteractionUseCase(
configuração: configuração,
private val transactionRepository:
InteractionRepository
): UseCase<UpdateInteractionUseCase.Request,
UpdateInteractionUseCase.Response>(configuração) {
…
}
7. No módulo do aplicativo, renomeie AppModule UseCaseModule .
8. No módulo app na classe UseCaseModule , forneça uma dependência para
GetPostsWithUsersWithInteractionUseCase :
@Módulo
@InstallIn(SingletonComponent::class)
class UseCaseModule {
…
@Provides
Diversão
provideGetPostsWithUsersWithInteractionUseCase(
configuração: UseCase.Configuration,
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 6/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
postRepository: PostRepository,
userRepository: UserRepository,
InteractionRepository: InteractionRepository
): GetPostsWithUsersWithInteractionUseCase =
GetPostsWithUsersWithInteractionUseCase(
configuração,
postRepositório,
userRepository,
interaçãoRepositório
)
}
Aqui nósprecisamos usar @Provides porque não estamos mais no mesmo mó-
dulo, o que significa que devemos tratar isso como uma dependência externa,
que precisa da anotação @Provides , semelhante à forma como fornecemos as
dependências Room e Retrofit.
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 7/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
class UseCaseModule {
…
@Provides
divertido fornecerGetUserUseCase(
configuração: UseCase.Configuration,
userRepository: UserRepository
): GetUserUseCase = GetUserUseCase(
configuração,
userRepository
)
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 8/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
id 'kotlin-android'
}
13. No mesmo arquivo, exclua os usos do Hilt do bloco de dependências :
dependências {
implementação(projeto(caminho: ":domínio"))
implementação coroutines.coroutinesAndroid
testImplementação test.junit
testImplementação test.coroutines
testImplementação test.mockito
}
14. Jogadaa classe RepositoryModule do pacote de injeção no módulo de repo‐
sitório de dados para o pacote de injeção no módulo de aplicativo e torne
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 9/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
}
18. Dentroa classe RepositoryModule , substitua o método bindPostRespository
por um método @Provides :
@Módulo
@InstallIn(SingletonComponent::class)
classe abstrata RepositoryModule {
@Provides
divertido fornecerPostRepository(
remotePostDataSource: RemotePostDataSource,
localPostDataSource: LocalPostDataSource
): PostRepository = PostRepositoryImpl(
remotePostDataSource,
localPostDataSource
)
…
}
Aqui, não podemos mais usar a anotação @Binds porque removemos a anota-
ção @Inject da classe PostRepositoryImpl e, por ser uma dependência ex-
terna, precisaremos usar @Provides .
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 10/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 11/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
Teste de instrumentação
dependências {
…
androidTestImplementação "androidx.test:core:1.4.0"
androidTestImplementação "androidx.test:runner:1.4.0"
androidTestImplementação "androidx.test:rules:1.4.0 "
AndroidTestImplementação
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 12/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
"androidx.test.ext:junit:1.1.3 "
AndroidTestImplementação
"androidx.test.espresso:espresso-core:3.4.0 "
androidTestImplementação "androidx.test.espresso.
inatividade: inatividade simultânea: 3.4.0 "
}
A seguiré um exemplo de um teste escrito usando Espresso:
@Teste
divertido meuTeste(){
ActivityScenario.launch(MainActivity::class.java)
.
moveToState(Lifecycle.State.RESUMED)
onView(withId(R.id.my_id))
.perform(clique())
.check(isExibido())
}
No exemplo anterior, usamos o método de inicialização ActivityScenario para
iniciar MainActivity e fazer a transição de Activity para o estado RESUMED . Em
seguida, usamos onView , que requer ViewMatcher , e withId procura View por
seu ID e retorna ViewMatcher contendo essas informações. Temos então a op-
ção de usar perform , que requer ViewAction . Isso é para quando queremos in-
teragir com determinadas visualizações. Também podemos executar ViewAs‐
sertion usando a verificaçãométodo. Nesse caso, estamos verificando se uma
visualização é exibida.
Outra adição útil para ajudar nos testes é o orquestrador. O orquestrador é útil
quando queremos excluir os dados gerados pelos testes que podem ser manti-
dos na memória ou persistidos no dispositivo e que, por sua vez, podem afetar
outros testes e causar mau funcionamento. O que o orquestrador faz é desins-
talar o aplicativo antes de cada teste executado para que cada teste seja em
um aplicativo recém-instalado. Para adicionar o orquestrador ao aplicativo,
você precisará adicioná-lo ao arquivo build.gradle do módulo:
andróide {
…
configuração padrão {
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 13/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
…
testInstrumentationRunnerArgumentos
clearPackageData: 'true'
opções de teste {
execução 'ANDROIDX_TEST_ORCHESTRATOR'
}
…
}
}
Isso adicionará oconfiguração do orquestrador na execução do teste e passe a
instrução para excluir os dados do aplicativo após cada teste. Para adicionar a
dependência do orquestrador ao projeto, é necessário o seguinte:
dependências {
…
androidTestUtil "androidx.test:orchestrator: 1.4.1"
}
O Espresso também vem com muitas extensões, uma das quais é o conceito de
IdlingResource . Quando os testes locais (testes executados na máquina de de-
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 14/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
countIdlingResource.decrement()
}
}
No exemplo anterior, temos CountingIdlingResource sendo incrementado no
início do método doOperation e decrementado após a operação longa que pre-
tendemos realizar. Para registrar e cancelar o registro de IdlingResource , po-
demos fazer o seguinte:
Como IdlingResource faz parte do Espresso, mas precisa ser usado quando as
operações dentro do código do aplicativo são executadas, queremos evitar o
uso de IdlingResource junto com esse código. Uma solução para isso é decorar
a classe que contém a operação e então usar injeção de dependência para inje-
tar a dependência decorada no teste. Para decorar o código, precisaremos ter
uma abstração para a operação. Um exemplo disso é o seguinte:
interface MinhaInterface {
diversão doOperation()
}
class MinhaClasse: MinhaInterface {
substituir fun doOperation() {
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 15/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
class MinhaClasseDecorada(
valor privado minhaInterface: MinhaInterface,
valor privado countIdlingResource:
Recurso de contagem em marcha lenta
): MinhaInterface {
substituir fun doOperation() {
countIdlingResource.increment()
minhaInterface.doOperation()
countIdlingResource.decrement()
}
}
Aqui, temos outra implementação de MyInterface , que conterá uma referência
à abstração e CountingIdlingResource . Quando doOperation for chamado, in-
crementaremos IdlingResource , chamaremos a operação e, quando terminar,
diminuiremos IdlingResource .
dependências {
androidTestImplementation "com.google.dagger:hilt-
teste android: 2.40.5"
kaptAndroidTest "com.google.dagger:hilt-android-
compilador: 2.40.5"
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 16/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
como o nome . Isso significa que quando o teste for executado, HiltTestApplica‐
tion será usado no lugar da classe Application que definimos em nosso código
andróide {
…
configuração padrão {
…
testInstrumentationRunner "com.test.MyTestRunner"
…
}
}
}
Isso permite que o teste instrumentado use o runner que criamos. Vamos
agora supor que temoso módulo a seguir, que fornecerá a dependência inicial:
@Módulo
@InstallIn(SingletonComponent::class)
classe abstrata MyModule {
@Binds
abstract fun bindMyClass(myClass: MyClass):
MyInterface
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 17/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
}
Aqui, estamos usando uma ligação simples para conectar uma implementação
à abstração. Na pasta androidTest , podemos criar um novo módulo no qual
substituímos esta instância pela decorada:
@Módulo
@TestInstallIn(
componentes = [SingletonComponent::class],
substitui = [MyModule::class]
)
class MyDecoratedModule {
@Provides
diversão fornecerIdlingResource() =
CountingIdlingResource("meu-recurso-ocioso")
@Provides
fun provideMyDecoratedClass(countingIdlingResource:
CountingIdlingResource) =
MyDecoratedClass(MyClass(), countIdlingResource)
}
NissoPor exemplo, usamos a anotação @TestInstallIn , que tornará as depen-
dências deste módulo ativas enquanto o aplicativo de teste e substituir as de-
pendências no módulo anterior. Podemos então fornecer dependências para
IdlingResource e MyDecoratedClass , que envolverão MyClass e usarão Idlin‐
gResource . Se quisermos que essas alterações entrem em vigor nos testes, pre-
@HiltAndroidTest
class MinhaAtividadeTeste {
@get:Regra(ordem = 0)
var hiltAndroidRule = HiltAndroidRule(this)
@Injetar
lateinit var idlingResource: CountingIdlingResources
@Antes da
configuração divertida(){
hiltAndroidRule.inject()
IdlingRegistry.getInstance().register
(recurso inativo)
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 18/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
}
@Depois
divertido tearDown(){
IdlingRegistry.getInstance().unregister
(recurso inativo)
}
}
NissoPor exemplo, usamos a anotação @HiltAndroidTest porque queremos in-
jetar CountingIdlingResources no teste. Em seguida, usamos HiltAndroidTes‐
tRule para realizar a injeção. Também demos a mais alta prioridade em ter-
mos de ordem de execução para regras de teste. Por fim, conseguimos regis-
trar e cancelar o registro de CountingIdlingResources para cada teste da classe.
O Jetpack Compose vem com suas próprias bibliotecas de teste, que exigem a
seguinte configuração para o arquivo build.gradle do módulo:
dependências {
androidTestImplementation "androidx.compose.ui:ui-
test-
junit4:1.0.5"
debugImplementation "androidx.compose.ui:ui-test-
manifesto: 1,0,5"
}
Para escrever testes para componentes do Jetpack Compose, precisaremos de-
finir uma regra de teste do Compose usando createComposeRule quando qui-
sermos testar métodos composáveis individuais ou createAndroidComposeRule
se quisermos testar o conteúdo do Compose de uma atividade inteira. Um
exemplo ficaria assim:
class MeuTeste {
@get:Regra
var composeTestRule = createAndroidComposeRule
(MinhaAtividade::class.java)
}
NoNo exemplo anterior, definimos uma regra de teste que será responsável
por testar o conteúdo do Compose dentro de MyActivity . Se quisermos que o
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 19/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
teste interaja com a interface do usuário ou afirme que ele exibe as informa-
ções corretas, temos a seguinte estrutura:
@Teste
divertido testDisplayList() {
composeTestRule.onNode()
.assertIsDisplayed()
.performClick()
}
Neste exemplo, usamos o método onNode para localizar um elemento especí-
fico, como Text ou Button . Temos então o método assertIsDisplayed , que é
usado para verificar se o nó é exibido. Por fim, temos o método performClick ,
que irá clicar no elemento. O Jetpack Compose usa seu próprio tipo IdlingRe‐
source , que pode ser registrado na regra de teste Compose, semelhante ao
exemplo a seguir:
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 20/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 21/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 22/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 23/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
androidTestJunit : "1.1.3",
orquestrador: "1.4.1"
]
…
}
2. No mesmo arquivo, façacertifique-se de que as seguintes dependências an‐
droidTest sejam adicionadas:
script de construção {
extensão {
…
teste android = [
junit: "androidx.test.ext
:junit:${versions.espressoJunit}",
espressoCore : "androidx.test.
espresso:espresso-core:${versions.
espressoCore}",
idlingResource: "androidx.test.
espresso:espresso-idling-resource
:${versions.espressoCore}",
composeUiTestJunit: "androidx.compose.
ui:ui-test-junit4:$
{versions.compose}",
composeManifest : "androidx.compose
.ui:ui-test-manifest:$
{versions.compose}",
hilt: "com.google.
punhal:hilt-android-testing:$
{versions.hilt}",
hiltCompiler : "com.google.
punhal:hilt-android-compiler:$
{versions.hilt}",
core: "androidx.test:
core:${versions.androidTestCore}",
corredor: "androidx.test:
corredor:$
{versions.androidTestCore}",
regras: "androidx.test:
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 24/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
regras:${versions.androidTestCore}"
,
orquestrador: "androidx.test:
orquestrador:$
{versions.orchestrator}"
]
}
…
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 25/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
return super.newApplication(cl,
HiltTestApplication::class.java.name,
contexto)
}
}
5. No arquivo build.gradle do módulo do aplicativo, defina a seguinte confi-
guração de teste. Façocertifique-se de substituir {package-name} pelo pacote
em que o PostAppTestRunner está:
andróide {
…
configuração padrão {
…
testInstrumentationRunner "{package-name}.
PostAppTestRunner"
testInstrumentationRunnerArgumentos
clearPackageData: 'true'
opções de teste {
execução 'ANDROIDX_TEST_ORCHESTRATOR'
}
}
}
6. Na pasta androidTest do módulo app, crie os seguintes pacotes dentro da
pasta java/{package-name} – idling , injection , remote , repository e test .
7. Lado de dentroo pacote idling , crie uma nova classe chamada Compose‐
CountingIdlingResource :
class ComposeCountingIdlingResource(name: String) :
Recurso Inativo {
valor privado countIdlingResource =
CountingIdlingResource(name)
substituir val isIdleNow: booleano
get() = countIdlingResource.isIdleNow
fun increment() = countIdlingResource.
incremento()
fun decrement() = countIdlingResource.
diminuir()
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 26/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
Isto éuma função de extensão que podemos usar para incrementar IdlingRe‐
source antes que Flow seja coletado e decrementá-lo quando o primeiro valor
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 27/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
.attachIdling(countingIdlingResource)
}
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 28/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
repositórios:
@Módulo
@TestInstallIn(
componentes = [SingletonComponent::class],
substitui = [RepositoryModule::class]
)
class IdlingRepositoryModule {
@Singleton
@Provides
diversão fornecerIdlingResource():
ComposeCountingIdlingResource =
Recurso ComposeCountingIdling
("repositório-inativo")
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 29/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
substitui = [RepositoryModule::class]
)
class IdlingRepositoryModule {
…
@Provides
divertido fornecerPostRepository(
remotePostDataSource: RemotePostDataSource,
localPostDataSource: LocalPostDataSource,
countIdlingResource:
Recurso ComposeCountingIdling
): PostRepository = IdlingPostRepository(
PostRepositoryImpl(
remotePostDataSource,
localPostDataSource
),
recurso de contagem
)
}
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 30/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 31/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
Aqui, criamos uma lista na qual retornamos dois usuários e colocamos no Flow
para o método getUsers .
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 32/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
Igual ao que fizemos com os usuários, criamos uma lista de posts e conecta-
mos os dois primeiros posts ao primeiro usuário e os dois últimos posts ao se-
gundo usuário.
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 33/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
res às abstrações:
@Módulo
@TestInstallIn(
componentes = [SingletonComponent::class],
substitui = [RemoteDataSourceModule::class]
)
classe abstrata MockRemoteDataSourceModule {
@Binds
diversão abstrata bindPostDataSource(
postDataSourceImpl: MockRemotePostDataSource):
RemotePostDataSource
@Binds
diversão abstrata
bindUserDataSource(userDataSourceImpl
: MockRemoteUserDataSource):
RemoteUserDataSource
}
20. No pacote de teste , crieuma classe chamada MainActivityTest :
@HiltAndroidTest
class MainActivityTest {
@get:Regra(ordem = 0)
var hiltAndroidRule = HiltAndroidRule(this)
@get:Regra(ordem = 1)
var composeTestRule = createAndroidComposeRule
(MainActivity::class.java)
@Injetar
lateinit var idlingResource:
Recurso ComposeCountingIdling
@Antes da
configuração divertida() {
hiltAndroidRule.inject()
composeTestRule.
registerIdlingResource(idlingResource)
}
@Depois
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 34/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
divertido desmontar() {
composeTestRule.unregisterIdlingResource
(recurso inativo)
}
}
Aqui nósestamos inicializando nossas regras de teste, que são para Hilt e Com-
pose, nessa ordem exata. Em seguida, injetamos ComposeCountingIdlingRe‐
source na classe de teste para que possamos registrá-la na regra de teste
Compose.
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 35/37
26/10/2022 21:47 Chapter 10: Putting It All Together | Clean Android Architecture
composeTestRule.onNodeWithText("Título:
título4")
.assertIsDisplayed()
}
}
Resumo
https://learning.oreilly.com/library/view/clean-android-architecture/9781803234588/B18320_10_ePub.xhtml 37/37