In android studio using kotlin, I created a class with the code below:
class Dice(val numSides:Int){
fun roll():Int{
return (1..numSides).random()
}
}
in ExampleUnitTest.kt file I have created the following code to test:
class ExampleUnitTest {
#Test
fun generates_number() {
val dice = Dice(4)
val rollResult = dice.roll()
assertTrue("The value of rollResult was not between 1 and 6", rollResult in 1..6)
}
}
The test should be passed only if Dice(6) as the result should be any number between 1 to 6 but as shown above when with Dice(4) or any number, the test passed. that why? thanks
Your test checks if the rolled number is between 1 and 6. With Dice(4) the result will be between 1 and 4, which means it's always going to be between 1 and 6 as well. Or to put it another way, it's impossible for you to get a value from Dice(4).roll() that isn't somewhere between 1 and 6 - it's never going to be 5 or 6, but that doesn't contradict anything!
What you probably want to test is whether Dice(4).roll()'s possible range of outputs is from 1 to 6. But there's no way to absolutely prove that from the outside, not the way the class is written anyway. All you can do is provide a value for numSides and then call roll() and check the output. That's all the class offers to you in terms of interactivity.
A better question is why you want to test this? Your test seems to be written to fail when your code is working - you should be testing that the behaviour is what you'd expect, so all your tests pass. So really, what you'd want to test is that Dice(4).roll() only produces values within the expected range, i.e. between 1 and 4.
Since it's random the only way to really do this is run the same test lots of times and make sure a bad number never comes up (or you eventually see a number you want), so you can say with a high degree of confidence that it's probably correct. Something like
#Test
fun generates_number() {
val dice = Dice(4)
val allGood = generateSequence { dice.roll() }.take(9999).all { it in 1..4 }
assertTrue("At least one dice roll was not between 1 and 4", allGood)
}
(or you could use a for loop with break to exit early if one of the values isn't valid, instead of the sequence + all)
I just picked 9999 at random but you could use statistics to pick a more suitable number. But the problem here is the random behaviour - because it's not predictable, you can't do a simple state in -> result out test with an expected result.
Related
i'm having a well known problem in android which is generating random numbers. the problem is that i need to generate 20 random numbers between 1 and 905 every time a screen is loaded. and if i use kotlin random it keeps generating the same sequence even if i'm closing and reopen the app. and java random and secure random generates different sequence only if i'm closing and reopening the app. but i want to have different sequences even if i dont close the app and open it again.
i know it has been asked before but my problem is that i tried every solution and its the same results.
i would appreciate the help if anyone knows what the hell is wrong.
here are the solutions i used:
1.
val randomIndex: List<Int> = List(20){
java.util.Random().nextInt(905) + 1
}
val randomIndex: MutableSet<Int> = remember{ mutableSetOf() }
while (randomIndex.size < 20){
randomIndex.add(java.util.Random().nextInt(905) + 1)
}
val randomIndex: List<Int> = (0..905).toList.shuffled(Random).subList(0,20)
i'm using jetpack compose and kotlin so ignore all the mutable state and remembers. also where ever you see Random i used all java Random, SecureRandom and kotlin Random and the problem i discussed persists.
i also tried initializing Random out of loop on the second code i put here and still it does the same thing.
EDIT:
thought i mentioned these here aswell. i'm using kolin 1.6.10 and my compile sdk for android is 33. i'm also trying the app on 2 different phones with android 8 and 11.
EDIT2:
#Tenfout04 suggested to put my indexes outside of my composable so this is what i did inside of my viewmodel:
var pokemonIndex: List<Int> = mutableStateListOf()
init {
pokemonIndex = List(20){
java.util.Random().nextInt(905)+1
}
}
and in my composable:
val viewModel: PokemonGameViewModel = hiltViewModel()
val randomIndex = viewModel.pokemonIndex
another thing i tried in my viewmodel is:
val pokemonIndex: MutableState<List<Int>>
get() = mutableStateOf(
List(20){
java.util.Random().nextInt(905)+1
}
)
results are the same :(
By default,
benchmarkRule.measureRepeated {
runs the test for 50 times with warmups.
Is there any way we can reduce the run to 10 or something?
Found out we can configure instrumentation arguments with
testInstrumentationRunnerArgument 'androidx.benchmark.startupMode.enable' , 'true'
that captures 10 measurements.
but this doesn't want "measureRepeated" to be used,
we get:
java.lang.AssertionError: Error - multiple benchmarks in startup mode.
Only one benchmark may be run per 'am instrument' call, to ensure
result isolation.
Any idea on how this can be taken ahead?
I find they way how you can decrease count :
#Before
fun init() {
val field = androidx.benchmark.BenchmarkState::class.java.getDeclaredField("REPEAT_COUNT")
field.isAccessible = true
field.set(benchmarkRule, 1)
}
Where 1 is the number of repeat count.
but you need to be careful - this can greatly affect the stable work for library
I have an Instrument test that has four match statements in it. There is one of the four statements that fails when ran with the other three, but passes if it runs by itself.
here is the test that fails
#Test
fun displayTypeFilteredPokemon(){
// When - PokemonList fragment launch and filtered by specific type
launchActivity()
onView(withId(R.id.genAllButton)).perform(click())
// Perform click action to do the filter on specific type
onView(withId(R.id.menu_filter)).perform(click())
onView(withText(R.string.label_type_electric)).perform(click())
// Then - Verify the list is filtered by the selected type
onView(withId(R.id.pokemon_list)).check(RecyclerViewItemCountAssertion(3))
onView(withText("Magnemite")).check(matches(isDisplayed()))
onView(withText("Jolteon")).check(matches(isDisplayed()))
onView(withText("Emolga")).check(matches(isDisplayed()))
}
here is the code for launch activity:
private fun launchActivity(): ActivityScenario<PokemonListActivity>? {
val scenario = launch(PokemonListActivity::class.java)
scenario.onActivity {
val intent = Intent()
intent.putExtra("genId",0)
it.intent = intent
}
return scenario
}
And here is the custom matcher code:
class RecyclerViewItemCountAssertion(private val matcher: Int) : ViewAssertion {
override fun check(view: View?, noViewFoundException: NoMatchingViewException?) {
if(noViewFoundException != null){
throw noViewFoundException
}
val recyclerView = view as RecyclerView
val adapter = recyclerView.adapter!!
assertThat(adapter.itemCount, IsEqual(matcher))
}
}
when using this set of matches it passes:
onView(withId(R.id.pokemon_list)).check(RecyclerViewItemCountAssertion(3))
onView(withText("Jolteon")).check(matches(isDisplayed()))
onView(withText("Emolga")).check(matches(isDisplayed()))
or when this set of matches it passes as well:
onView(withText("Magnemite")).check(matches(isDisplayed()))
here is the view being tested:
I am slightly confused because the view clearly has the matching text there. Could it be the resource isn't idling, therefor the test just closes? For example the reason the test with only one matcher passes is due to being fast enough to match before it finishes?
I have thought about introducing EspressoIdlingResource but I have read that introduces difficulties in a code base and I would like to keep it simple for learning purposes.
Another indicator that I think the race condition is an issue is that when I debug the test it passes. When I just run the test it fails.
Edit 1:
when running all the test by itself, I have no errors. When running all the tests in the class I get the following error:
androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.stegner.androiddex:id/menu_filter
Your approach to writing espresso tests seems wrong.
While writing a UI test, what we want to confirm is the following.
"If user interacts clicks button X, swipes the page, writes 500 to this edittext, and clicks button Y, he will be able to see text Z".
As you can see, we are testing the app as if we are the end user and by just interacting with the elements we see in the screen.
private fun launchActivity(): ActivityScenario<PokemonListActivity>? {
val scenario = launch(PokemonListActivity::class.java)
scenario.onActivity {
val intent = Intent()
intent.putExtra("genId",0)
it.intent = intent
}
return scenario
}
When you use launchActivity and pass a data with an intent, you disregard what we want to test,end users can't pass data with intents, they just click to a button which triggers our code to pass the data with intents. So a good test, should be from end to end (from launch screen to the screen you want) with mimicking user's movements(click X, click Y, write 500 to Z etc)
Now your problem is most likely resulted from using launchActivity in every single one of your tests inside a method, the second test can't launch the Activity (at least it can't launch the activity fast enough) so you get an error in your first assertion.
androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with id: com.stegner.androiddex:id/menu_filter
You should remove launchActivity() from your test methods and define an activity test rule using the first screen your app displays.
#Rule
public ActivityTestRule<MyFirstActivity> activityRule =
new ActivityTestRule<>(MyFirstActivity.class);
When you annotate this class with the annotation Rule, before every test starts it will make sure this activity is loaded and then you can go to the page you want with mimicking user interactions. If you still have problems after applying this solution, please write it as a comment because then your problem will be related to synchronization which requires a different answer.
I am working with Espresso in android at the moment, and I have this test, which is clicking a button, populating my listView and then logging the execution time of the action.
#RunWith(AndroidJUnit4::class)
class Test {
#get:Rule
val mActivityActivityTestRule = ActivityTestRule(MainActivity::class.java)
#Test
#Throws(java.lang.Exception::class)
fun test_fetchData_populates_listView() {
//Click the get_rec_articles button.
onView(withId(R.id.get_rec_articles_btn)).perform(click())
val start = System.currentTimeMillis()
//check if the listView is created.
onView(withId(R.id.rec_articles_listview)).check(matches(isDisplayed()))
//check if items can be clicked.
onData(CoreMatchers.anything()).inAdapterView(withId(R.id.rec_articles_listview)).atPosition(0).perform(click())
val end = System.currentTimeMillis()
val loadTime = (end - start)
Log.d("###### LOADTIME FOR FETCHDATA FUNCTION:", "" + loadTime + " MILLISECS ######")
}
}
I would like to find a good way to call the test 50 times and then get the average load time. I could just run it manually 50 times and then calculate it, but I have several tests which I would like the average load times from.
I have read the question on the site about the same topic and tried the suggested solutions, but it didn't help.
I had a very similar issue and as a result I've created a library to run Android UI tests multiple times. Might be useful in your case: https://github.com/stepstone-tech/AndroidTestXRunner
As far as the load time goes, after running tests there's a JUnit report created with times of each test being placed in a generated JUNit report XML. You could get those values and calculate an average based on that.
I playing around with Kotlin and Coroutines in my demo android application.
Here's what I have:
fun testCoroutine3() = runBlocking {
var num = 0
val jobs = List(10_000) { // create a lot of coroutines and list their jobs.
launch(CommonPool) {
delay(1000L)
println(num++)
}
}
for(job in jobs) {
job.join() //wait for all jobs to finish
}
println("FINAL RESULT $num")
}
Basically I'm creating a list of 10,000 Coroutines that wait for 1 second and print a number then increment it.
Then when all jobs are done I print the final result.
(This demo is taken from the GitHub Documentation)
Now most of my test run fine, all the coroutines run almost simultaneously, and my final result is 10000
However in some rare occasions, I am getting the final result as 9,999
This become more obvious when I increase the number to 50,000 for example:
Is it possible that Kotlin is skipping some coroutines when there's a lot of them? on the 50,000, looks like it skipped 2
Or is something else happening here?
num++ consists of two operations: tmp = num + 1 and num = tmp. When dealing with multithreading like your example there are cases where some operations might overwrite the results of another thread, leading to cases like your example.
If you want to know more, research "race conditions" where the end result depends on a "race" between two seperate processes.