Olá, neste post, iremos dar sequência a nosso projeto de apresentação.
Os links anteriores são:
Parte 1: https://medium.com/@nicolaugalves/android-cwb-iniciando-projeto-c3a2c5670879
Parte 2: https://medium.com/@nicolaugalves/android-cwb-parte-2-fbf44ef004cf
Parte 3: https://medium.com/@nicolaugalves/android-cwb-parte-3-a0c856c22d8d
Paramos no uso do Coroutines anteriormente. Neste post vamos mudar a nossa chamada para buscar no banco de dados Room.
Vamos no gradle.app
Adicionar
apply plugin: 'kotlin-kapt'
e nas dependências
// Room components
implementation "androidx.room:room-runtime:2.1.0"
kapt "androidx.room:room-compiler:2.1.0"
androidTestImplementation "androidx.room:room-testing:2.1.0"
ao meu IGetUserDataSource
adicionei
fun getUserDB(): User
meu GetUserDataSource
override fun getUserDB(): User {
return User(true)
}
Agora vamos devolver pelo BD e não mais um User(true)
Criei as classes do BD no meu framework
Essa configuração é a seguinte.
AndroidCWBRoom é onde criamos nosso banco.
UserDao tem os acessos para os métodos que vai implementar ex: insert, delete, update, get. Fiz um exemplo também para uma chamada com Query, as vezes o programador se confunde com isso. Vai ai o help desta parte:
@Query(
"SELECT * from user WHERE token = :token AND didLogin = :didLogin")
fun getUserWithTokenAndDidLogin(token: String, didLogin: Boolean): List<UserEntity>
o UserEntity é diferente do User, pois ele pode ter informações que depois vão vir do endpoint então temos que fazer um mapeamento entre UserEntity e User. Por isso a classe UserEntitiyMapper.
A princípio o método fica igual criar uma instancia de User. Depois vou inserir um UserEntity no banco de dados local e fazer esse maper. Aguenta aí.
class UserEntityMapper { companion object {
fun transformToUser(user: UserEntity?) : User {
return User(true)
}
}
}
E, para finalizar, precisamos colocar ele no AndroidCWBApplication na injeção. Agora você vai perceber que estou colocando 2 vezes o getDB() e isso é proposital. Assim que terminar esse exemplo vou implementar o MVVM com Factory para ele lidar com a criação das instancias.
private fun injectDependencies() { AndroidCWBMvpFactory.inject(
getDb(),
AppDispatcherProvider().io(),
AppDispatcherProvider().ui(),
Interactors(
getUserUseCaseImpl()
)
)
}private fun getUserUseCaseImpl(): GetUserUseCaseImpl {
return GetUserUseCaseImpl(
GetUserRepositoryImpl(
GetUserDataSource(
getDb()
)
)
)
}
Meu GetUserDataSource, ficou assim,
class GetUserDataSource(
private val db: AndroidCWBRoom
) : IGetUserDataSource { override fun execute(username: String, password: String): User? {
return getUserDB()
} override fun getUserDB(): User? {
val users = db.getUserDAO().getAllUser()
try {
if (users.isNotEmpty()) {
return UserEntityMapper.transformToUser(users.first())
} else {
if (insertUserEntityInDbAndReturnUser(users))
return UserEntityMapper.transformToUser(users.first()
)
}
} catch (e: Exception) {
throw Exception("Somenthing WRONG happens! hehe")
} return null
} private fun insertUserEntityInDbAndReturnUser(users: List<UserEntity>): Boolean {
var users1 = users
db.getUserDAO().insert(
UserEntity(didLogin = true, user = "AndroidCWB", token = "meutokenfalso")
) users1 = db.getUserDAO().getAllUser() if (users1.isNotEmpty()) {
return true
}
return false
}
}
Explicando: Caso não tenha um User, insere, caso já tenha, usa esse mesmo.
E o meu teste atualizou, e continua passando, eee ta pensando o que? hehe.
class GetUserDataSourceTest { private val dbRoom: AndroidCWBRoom = Mockito.mock(AndroidCWBRoom::class.java)
private val userDAO: UserDAO = Mockito.mock(UserDAO::class.java) @Test
fun whenGetUserDataSourceCall_shoulCallDbRoom() { Mockito.`when`(dbRoom.getUserDAO()).thenReturn(userDAO) val getUserDataSource = GetUserDataSource(
dbRoom
) getUserDataSource.execute("", "")
Mockito.verify(dbRoom, Mockito.atLeast(1)).getUserDAO()
Mockito.verifyNoMoreInteractions(dbRoom)
}
}
É isso, agora vamo fazer o insert do User caso ele não tenha pelo menos 1 usuário no BD para podermos usar ele. Depois, lógico, vamos pegar esse usuário pela internet.
Assim, temos que criar nosso teste instrumentado para validar se o BD ta fazendo o insert mesmo.
Criando a classe no androidTest fica assim:
@RunWith(AndroidJUnit4::class)
class UserDAORoomTest { private lateinit var androidCWBRoom: AndroidCWBRoom
private lateinit var userDAO: UserDAO @Test
fun useAppContext() {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Assert.assertEquals("carlos.nicolau.galves.androidcwb", appContext.packageName)
} @Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
androidCWBRoom = AndroidCWBRoom.getDatabase(context)
userDAO = androidCWBRoom.getUserDAO()
} @Test
fun whenInsertUser_checkIfDbHaveOneUser() {
androidCWBRoom.getUserDAO().insert(
UserEntity(didLogin = true, user = "AndroidCWB", token = "meutokenfalso")
) Assert.assertNotNull(
userDAO.getAllUser()
)
} @Test
fun whendeleteAlltUser_checkIfDbIsEmpty() {
androidCWBRoom.getUserDAO().deleteAll()
val list = userDAO.getAllUser() Assert.assertTrue(list.isEmpty())
} @After
fun closeDb() {
androidCWBRoom.close()
}
}
Nele estamos testando se realmente insere um UserEntity, depois vamos criando os demais testes, pegou a ideia né?
Também to verificando o excluir. Isso porque no meu código de produção, eu vou mostrar que na primeira vez, ele insere e retorna, e na segunda ele só pega o primeiro user. Com o teste integrado eu validei o delete para ajudar a chegar na solução de insert único.
Rodando os testes intrumentados o resultado
Estamos mantendo o controle de nosso código de produção.
Agora vamos validar o app para ver se adiciona um user, vou mostrar com o debug para onde ele aponta.
Primeira vez:
Nosso mapper acionado com 1 objeto já criado:
Agora na segunda vez:
Pronto..
Mais a frente, nós vamos criar um método para inserir os bonecos do android, com um json local. Mas vamos por parte que tudo terá seu tempo para ser demonstrado.
Bom, acho que é isso, no próximo capitulo vamos usar o ViewModel para ajudar a tratar os estados da tela. Vejo você lá! Valeu.
Att, Carlos Nicolau Galves.