Improve tests

This commit is contained in:
Phillip Thelen 2022-03-23 16:06:23 +01:00 committed by Phillip Thelen
parent 60ec4c0533
commit e60ff5a303
11 changed files with 225 additions and 24 deletions

View file

@ -1,7 +1,10 @@
package com.habitrpg.android.habitica.ui.fragments
import androidx.fragment.app.Fragment
import androidx.fragment.app.testing.FragmentScenario
import androidx.viewbinding.ViewBinding
import com.habitrpg.android.habitica.api.GSonFactoryCreator
import com.habitrpg.android.habitica.data.ContentRepository
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.data.TutorialRepository
@ -9,7 +12,11 @@ import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.BaseObject
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.ContentResult
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.Food
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
@ -23,8 +30,9 @@ import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
import org.junit.Before
import java.io.InputStreamReader
abstract class FragmentTestCase<F : BaseFragment<VB>, VB : ViewBinding, S : Screen<S>> : TestCase() {
abstract class FragmentTestCase<F : Fragment, VB : ViewBinding, S : Screen<S>> : TestCase() {
lateinit var scenario: FragmentScenario<F>
lateinit var fragment: F
@ -34,6 +42,7 @@ abstract class FragmentTestCase<F : BaseFragment<VB>, VB : ViewBinding, S : Scre
val socialRepository: SocialRepository = mockk(relaxed = true)
val tutorialRepository: TutorialRepository = mockk(relaxed = true)
val appConfigManager: AppConfigManager = mockk(relaxed = true)
val contentRepository: ContentRepository = mockk(relaxed = true)
val userViewModel: MainUserViewModel = mockk(relaxed = true)
abstract fun makeFragment()
@ -50,8 +59,9 @@ abstract class FragmentTestCase<F : BaseFragment<VB>, VB : ViewBinding, S : Scre
fun setUpFragment() {
clearAllMocks()
user = User()
user.stats = Stats()
val userStream = javaClass.classLoader?.getResourceAsStream("user.json")
val gson = GSonFactoryCreator.createGson()
user = gson.fromJson<User>(gson.newJsonReader(InputStreamReader(userStream)), User::class.java)
user.stats?.lvl = 20
user.stats?.points = 30
every { userRepository.getUser() } returns userEvents
@ -60,6 +70,21 @@ abstract class FragmentTestCase<F : BaseFragment<VB>, VB : ViewBinding, S : Scre
throw errorSlot.captured
}
every { socialRepository.getUnmanagedCopy(capture(unmanagedSlot)) } answers { unmanagedSlot.captured }
val contentStream = javaClass.classLoader?.getResourceAsStream("content.json")
val reader = gson.newJsonReader(InputStreamReader(contentStream))
val content = gson.fromJson<ContentResult>(reader, ContentResult::class.java)
every { inventoryRepository.getPets() } returns Flowable.just(content.pets)
every { inventoryRepository.getItems(Food::class.java) } returns Flowable.just(content.food)
every { inventoryRepository.getItems(Egg::class.java) } returns Flowable.just(content.eggs)
every { inventoryRepository.getItems(HatchingPotion::class.java) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItems(QuestContent::class.java) } returns Flowable.just(content.quests)
every { inventoryRepository.getItems(Food::class.java, any()) } returns Flowable.just(content.food)
every { inventoryRepository.getItems(Egg::class.java, any()) } answers {
Flowable.just(content.eggs)
}
every { inventoryRepository.getItems(HatchingPotion::class.java, any()) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItems(QuestContent::class.java, any()) } returns Flowable.just(content.quests)
makeFragment()
}
}

View file

@ -0,0 +1,65 @@
package com.habitrpg.android.habitica.ui.fragments
import android.view.View
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.filters.LargeTest
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DrawerMainBinding
import io.github.kakaocup.kakao.recycler.KRecyclerItem
import io.github.kakaocup.kakao.recycler.KRecyclerView
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.kakao.text.KTextView
import io.mockk.spyk
import org.hamcrest.Matcher
import org.junit.Test
class SectionHeaderItem(parent: Matcher<View>) : KRecyclerItem<SectionHeaderItem>(parent) {
val title = KTextView(parent) { isRoot() }
}
class MainItem(parent: Matcher<View>) : KRecyclerItem<MainItem>(parent) {
val title = KTextView(parent) { withId(R.id.titleTextView) }
}
class NavigationDrawerScreen : Screen<NavigationDrawerScreen>() {
val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView)
}, itemTypeBuilder = {
itemType(::SectionHeaderItem)
itemType(::MainItem)
})
}
@LargeTest
internal class NavigationDrawerFragmentTest : FragmentTestCase<NavigationDrawerFragment, DrawerMainBinding, NavigationDrawerScreen>() {
override fun makeFragment() {
scenario = launchFragmentInContainer(null, R.style.MainAppTheme) {
fragment = spyk()
fragment.userRepository = userRepository
fragment.inventoryRepository = inventoryRepository
fragment.socialRepository = socialRepository
fragment.contentRepository = contentRepository
fragment.configManager = appConfigManager
return@launchFragmentInContainer fragment
}
}
override val screen = NavigationDrawerScreen()
@Test
fun showsMenuItems() {
screen {
recycler {
isVisible()
childWith<MainItem> { withDescendant { withText("Tasks") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Market") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Items") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Purchase Gems") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Subscription") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Party") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Tavern") } }.isVisible()
childWith<MainItem> { withDescendant { withText("Support") } }.isVisible()
}
}
}
}

View file

@ -0,0 +1,62 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.items
import android.view.View
import androidx.fragment.app.testing.launchFragmentInContainer
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
import com.habitrpg.android.habitica.ui.fragments.FragmentTestCase
import io.github.kakaocup.kakao.recycler.KRecyclerItem
import io.github.kakaocup.kakao.recycler.KRecyclerView
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.kakao.text.KTextView
import io.mockk.every
import io.mockk.spyk
import io.reactivex.rxjava3.core.Flowable
import org.hamcrest.Matcher
import org.junit.Test
class ItemItem(parent: Matcher<View>) : KRecyclerItem<ItemItem>(parent) {
val title = KTextView(parent) { withId(R.id.titleTextView) }
val owned = KTextView(parent) { withId(R.id.ownedTextView) }
}
class ItemScreen : Screen<ItemScreen>() {
val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView)
}, itemTypeBuilder = {
itemType(::ItemItem)
})
}
internal class ItemRecyclerFragmentTest : FragmentTestCase<ItemRecyclerFragment, FragmentRecyclerviewBinding, ItemScreen>() {
override fun makeFragment() {
every { inventoryRepository.getOwnedItems("eggs") } answers {
Flowable.just(user.items?.eggs!!.filter { it.numberOwned > 0 })
}
scenario = launchFragmentInContainer(null, R.style.MainAppTheme) {
fragment = spyk()
fragment.shouldInitializeComponent = false
fragment.userRepository = userRepository
fragment.inventoryRepository = inventoryRepository
fragment.socialRepository = socialRepository
fragment.userViewModel = userViewModel
fragment.itemType = "eggs"
return@launchFragmentInContainer fragment
}
}
override val screen = ItemScreen()
@Test
fun hasOnlyOwnedItems() {
screen {
recycler {
isVisible()
children<ItemItem> {
isVisible()
owned.hasNoText("0")
}
}
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"worldBoss":{},"currentEvent":{"event":"noCurrentEvent","start":"2022-02-18T20:00-05:00","end":"2022-03-31T20:00-05:00","season":"normal","npcImageSuffix":""},"npcImageSuffix":"","currentEventList":[{"event":"noCurrentEvent","start":"2022-02-18T20:00-05:00","end":"2022-03-31T20:00-05:00","season":"normal","npcImageSuffix":""}]}

View file

@ -70,24 +70,39 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class GSonFactoryCreator {
public static GsonConverterFactory create() {
Type skillListType = new TypeToken<List<Skill>>() {}.getType();
Type taskTagClassListType = new TypeToken<RealmList<Tag>>() {}.getType();
Type customizationListType = new TypeToken<RealmList<Customization>>() {}.getType();
Type tutorialStepListType = new TypeToken<RealmList<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<RealmList<FAQArticle>>() {}.getType();
Type itemDataListType = new TypeToken<RealmList<Equipment>>() {}.getType();
Type questCollectListType = new TypeToken<RealmList<QuestCollect>>() {}.getType();
Type chatMessageListType = new TypeToken<RealmList<ChatMessage>>() {}.getType();
Type challengeListType = new TypeToken<List<Challenge>>() {}.getType();
Type challengeRealmListType = new TypeToken<RealmList<Challenge>>() {}.getType();
Type questDropItemListType = new TypeToken<RealmList<QuestDropItem>>() {}.getType();
Type ownedItemListType = new TypeToken<RealmList<OwnedItem>>() {}.getType();
Type ownedPetListType = new TypeToken<RealmList<OwnedPet>>() {}.getType();
Type ownedMountListType = new TypeToken<RealmList<OwnedMount>>() {}.getType();
Type achievementsListType = new TypeToken<List<Achievement>>() {}.getType();
public static Gson createGson() {
Type skillListType = new TypeToken<List<Skill>>() {
}.getType();
Type taskTagClassListType = new TypeToken<RealmList<Tag>>() {
}.getType();
Type customizationListType = new TypeToken<RealmList<Customization>>() {
}.getType();
Type tutorialStepListType = new TypeToken<RealmList<TutorialStep>>() {
}.getType();
Type faqArticleListType = new TypeToken<RealmList<FAQArticle>>() {
}.getType();
Type itemDataListType = new TypeToken<RealmList<Equipment>>() {
}.getType();
Type questCollectListType = new TypeToken<RealmList<QuestCollect>>() {
}.getType();
Type chatMessageListType = new TypeToken<RealmList<ChatMessage>>() {
}.getType();
Type challengeListType = new TypeToken<List<Challenge>>() {
}.getType();
Type challengeRealmListType = new TypeToken<RealmList<Challenge>>() {
}.getType();
Type questDropItemListType = new TypeToken<RealmList<QuestDropItem>>() {
}.getType();
Type ownedItemListType = new TypeToken<RealmList<OwnedItem>>() {
}.getType();
Type ownedPetListType = new TypeToken<RealmList<OwnedPet>>() {
}.getType();
Type ownedMountListType = new TypeToken<RealmList<OwnedMount>>() {
}.getType();
Type achievementsListType = new TypeToken<List<Achievement>>() {
}.getType();
Gson gson = new GsonBuilder()
return new GsonBuilder()
.registerTypeAdapter(taskTagClassListType, new TaskTagDeserializer())
.registerTypeAdapter(Boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(boolean.class, new BooleanAsIntAdapter())
@ -122,6 +137,8 @@ public class GSonFactoryCreator {
.registerTypeAdapter(SocialAuthentication.class, new SocialAuthenticationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);
}
public static GsonConverterFactory create() {
return GsonConverterFactory.create(createGson());
}
}

View file

@ -29,7 +29,6 @@ import com.habitrpg.android.habitica.models.inventory.SpecialItem
import com.habitrpg.android.habitica.models.responses.SkillResponse
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity
@ -63,7 +62,6 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
var itemType: String? = null
var transformationItems: MutableList<OwnedItem> = mutableListOf()
var itemTypeText: String? = null
var user: User? = null
private var selectedSpecialItem: SpecialItem? = null
internal var layoutManager: androidx.recyclerview.widget.LinearLayoutManager? = null