How to do Sequential Tests with Espresso Kotlin - kotlin

So I want to make my Practice test suite sequential, meaning, tests build on top of each other. Currently its restarting the app every time a test finishes, but I would like for the app to remain open.
I've tried using #BeforeAll but its not working, it makes me add JUnit5.4 to the class path and even after I do It still red, meaning It doesn't like it for some reason.
Anyway, I think its the rule, I think the activity is making my tests restart every time each one finishes, id like it to not do that or if there's a different test rule that I can use that doesn't do that then that would be magnificent.
class Practice {
#get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java);
#Before
fun setUp() {
onView(withText("Log In With Mobile Code")).perform(click());
onView(withResourceName("txtMobileAccessCode")).check(matches(withText("Company Code
or\nMobile Access Code")));
}
#Test
fun clickOnEnterAccessCode() {
onView(withResourceName("txtCodeEntry")).perform(typeText("CodeGoesHere"));
}
#Test
fun enterCode() {
onView(withResourceName("btnCodeSubmit")).perform(click());
}
}

The problem is in using an ActivityScenarioRule to drive things; this calls ActivityScenario.close() by default at the end of each test, which will reset your app.
Instead, look at controlling the activity's lifecycle yourself by dealing directly with ActivityScenario. There will be more overhead, but you'll have much more control over things and be able to not call close() until you want to.
From the Android Developer Docs the syntax you're looking for is the following:
val scenario = launchActivity<MainActivity>()

Thank you Mike Collins, couldn't have done this without you.
This is the end product, with this is possible to make my whole suite sequential I only have to declare scenario in the first test and the rest keep using the activity.
#Before
fun setUp() {
val scenario = launchActivity<MainActivity>()
Espresso.onView(ViewMatchers.withText("Log In With Mobile
Code")).perform(ViewActions.scrollTo())
Espresso.onView(ViewMatchers.withText("Log In With Mobile
Code")).perform(ViewActions.click());
Espresso.onView(ViewMatchers.withResourceName("txtMobileAccessCode"))
.check(ViewAssertions.matches(ViewMatchers.withText("Company Code or\nMobile
Access Code")));
}
And with this I dont even have to use
#get:Rule
val activityRule = ActivityScenarioRulePractice(MainActivity::class.java);
I can leave that out all together
class Test1 {
#Before
fun setUp() {
val scenario = launchActivity<MainActivity>()
Espresso.onView(ViewMatchers.withText("Log In With Mobile Code")).perform(ViewActions.scrollTo())
Espresso.onView(ViewMatchers.withText("Log In With Mobile Code")).perform(ViewActions.click());
Espresso.onView(ViewMatchers.withResourceName("txtMobileAccessCode")).check(ViewAssertions.matches(ViewMatchers.withText("Company Code or\nMobile Access Code")));
}
#Test
fun clickOnEnterAccessCode() {
Espresso.onView(ViewMatchers.withResourceName("txtCodeEntry")).perform(ViewActions.typeText("Bluebook"));
Espresso.onView(ViewMatchers.withResourceName("btnCodeSubmit")).perform(ViewActions.click());
Espresso.onView(ViewMatchers.withText("<b>Network connection unavailable</b><br/>\n" +
"Please check your network settings and try again.")).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
}
#After
fun cleanUp() {
reportHelper.label("Stopping App");
}
}
I might have to put something on the #After but so far its closing everything automatically.

Related

Compose Desktop testing - how to check if something is visible?

Given some simple content:
#Composable
fun MyContent() {
var showThing by remember { mutableStateOf(false) }
if (showThing) {
Box(Modifier.testTag("thing")) {
Text("The Thing")
}
}
}
If I try to test whether the thing has been displayed:
#OptIn(ExperimentalTestApi::class)
class Scratch {
#get:Rule
val compose = createComposeRule()
#Test
fun test() {
runBlocking(Dispatchers.Main) {
compose.setContent {
MyContent()
}
compose.awaitIdle()
compose.onNodeWithTag("thing").assertIsNotDisplayed()
}
}
}
I get this:
An operation is not implemented.
kotlin.NotImplementedError: An operation is not implemented.
at androidx.compose.ui.test.DesktopAssertions_desktopKt.checkIsDisplayed(DesktopAssertions.desktop.kt:23)
at androidx.compose.ui.test.AssertionsKt.assertIsNotDisplayed(Assertions.kt:49)
at Scratch$test$1.invokeSuspend(Scratch.kt:44)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
...
I thought testing whether something was displayed or not would be the most basic thing to test, but it isn't supported by the framework yet. The test framework is experimental, so I was expecting to find things missing, but not like this.
Is there another way to do this which I'm missing? All the tutorials out there talk about assertIsDisplayed() being the way, but maybe there is an alternative?
It's not a direct substitute, but unfortunately, JB Compose Desktop has these limitations in the UI test suite. Aside from using only JUnit 4, and not being compatible with the newer version, many assertion methods and also screen interaction methods are not implemented, such as the .assertIsNotDisplayed() that you tried to use, and also actions like .performTextInput().
An alternative for your problem would be using other methods like .assertDoesNotExist() and .assertExists().
It's not going to tell you if the element is in the boundaries of the screen and visible, but at least will tell you that your node exists and is instantiated, which is something, and it's better than nothing.
Until JetBrains implement the complete desktop test suite, we need to work with what we have, or maybe try implementing some things as a workaround.
In your case, this will work:
#OptIn(ExperimentalTestApi::class)
class Scratch {
#get:Rule
val compose = createComposeRule()
#Test
fun test() {
runBlocking(Dispatchers.Main) {
compose.setContent {
MyContent()
}
compose.awaitIdle()
compose.onNodeWithTag("thing").assertDoesNotExist()
}
}

Kotlin state flows not emitting in unit test with view model scope

I'm new to Kotlin Coroutines and Flows and unit testing them. I have a pretty simple test:
#Test
fun debounce(): Unit = runBlocking {
val state = MutableStateFlow("hello")
val debouncedState = state.debounce(500).stateIn(this, SharingStarted.Eagerly, "bla")
assertThat(debouncedState.value).isEqualTo("bla")
state.value = "good bye"
// not yet...
assertThat(debouncedState.value).isEqualTo("bla")
delay(600)
// now!
assertThat(debouncedState.value).isEqualTo("good bye")
// cannot close the state flows :(
cancel("DONE")
}
It works just fine (except that I cannot stop it, but that's a different issue).
Next, I want to test my ViewModel which includes the exact same state flows. It's basically the same code above, but I thought it should run in the same scope as viewModel.someMutableStateFlow so I tried to run it on viewModelScope:
#Test
fun debounce(): Unit = runBlocking {
val viewModel = MyViewModel()
// in view model the state and debouncedState are defined the same way as above
val state = viewModel.someMutableStateFlow
val debouncedState = state.debounce(500).stateIn(viewModel.viewModelScope, SharingStarted.Eagerly, "bla")
///////////////////////////////////////////////////
// Below is the same code as in previous example //
///////////////////////////////////////////////////
assertThat(debouncedState.value).isEqualTo("bla")
state.value = "good bye"
// not yet...
assertThat(debouncedState.value).isEqualTo("bla")
delay(600)
// now!
assertThat(debouncedState.value).isEqualTo("good bye")
// cannot close the state flows :(
cancel("DONE")
}
But this time the debouncedState.value is never changed, it stays bla all the time! Nothing is emitted from those states.
Does this have something to do with the fact that I am using viewModelScope and maybe it is not running?
Some explanation about what's going on here would be great.
From this documentation about setting the Main dispatcher:
However, some APIs such as viewModelScope use a hardcoded Main dispatcher under the hood.
It then describes how to replace the Main dispatcher with a TestDispatcher:
class HomeViewModelTest {
#Test
fun settingMainDispatcher() = runTest {
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
Dispatchers.setMain(testDispatcher)
try {
val viewModel = HomeViewModel()
viewModel.loadMessage() // Uses testDispatcher, runs its coroutine eagerly
assertEquals("Greetings!", viewModel.message.value)
} finally {
Dispatchers.resetMain()
}
}
}
To quote the article:
If the Main dispatcher has been replaced with a TestDispatcher, any newly-created TestDispatchers will automatically use the scheduler from the Main dispatcher, including the StandardTestDispatcher created by runTest if no other dispatcher is passed to it.
The documentation also describes how to make a test rule so you don't have to do this in each test.

Junit - how to get test result as string?

I am trying to test the code and get a result as String to send it to API later.
class FirstClass{
fun main(){
print("Hello world!")
}
}
Test:
ort org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import kotlin.test.assertEquals
internal class FirstClassTest {
private val outContent = ByteArrayOutputStream()
private val errContent = ByteArrayOutputStream()
private val originalOut = System.out
private val originalErr = System.err
#BeforeEach
fun setUp() {
System.setOut(PrintStream(outContent))
System.setErr(PrintStream(errContent))
}
#AfterEach
fun tearDown() {
System.setOut(originalOut)
System.setErr(originalErr)
}
#Test
fun main() {
val SUT = FirstClass()
SUT.main()
val testResult = assertEquals("Hello world!", outContent.toString())
print("Test result: $testResult")
val api = Api()
val apiResult = api.sendResult(testResult.toString())
print("Api result: $apiResult")
}
}
The test is passing, however, I do not see printed messages. How to get a test result as String?
There are several issues here.  The main one is:
The redirection affects your test method too.
Because you've redirected System.out, the print() in your test method goes to outContent, along with the output from FirstClass.main() that you want to test, instead of to the screen or wherever you want it.
I can see two fixes for this.
The quick one is for your test method to output to originalOut:
originalOut.print("Test result: $testResult")
Your test method is in the class which does the redirection, so there's no problem with it knowing about the redirection, and it already has access to originalOut.
However, if you can, I think a better solution is to refactor FirstClass so that it doesn't hard-code the stream it writes to.  For example, the stream could be passed as a parameter; or it could return the string directly (and the caller, in a thin non-tested wrapper, could write it to System.out).
That would be more work, but would make your code more flexible as well as easier to test.
Other issues include:
You're using print() instead of println().
Many streams are line-buffered, writing their output only after a newline, and so you might not see any results if there isn't one.  (And even if you do, all the results would be jammed on a single line!)
You assign the result of assertEquals().
assertEquals() doesn't have a useful return value.  (It return Unit.)  So your code will simply show:
Test result: kotlin.Unit
Instead, like all the assert functions, it throws an exception if the assertion fails.  So there's no point in storing or processing the return value; simply calling the assertion is enough.
— This means that there's usually no need to call print()/println() from your test method anyway!  If there's a failure, it'll be obvious: running from the command line will stop with an exception message and stack trace; IntelliJ shows a big red mark next to that test; Maven and Gradle will stop the build (after all tests have run), showing the number of failures.  So if everything continues smoothly, you know the tests have passed.
Api is not defined.
The code you posted above won't compile, because it doesn't include a definition or import for Api.  (Those last lines can be removed, though, without affecting the question.)
main() is a confusing name for a test.
The unit testing framework will find and run all test methods annotated with #Test.  A test class will often contain many different test methods, and it's usual to name them after the aspect they're testing.  (That makes any failures clearer.)  Calling it main() not only fails to describe what's being tested, but also suggests that the method will be run from outside the testing framework, which would probably not behave properly.

Espresso test: How to open my application back after opening the recent apps?

I want to open my application back while writing an Espresso test case after opening recent apps by calling pressRecentApps() method.
Is there a way to do this except of simulating a click by coordinates?
I'd say that you can't. The moment your app loses focus, you are out of luck.
You can use UI Automator for that.
You can do this with Espresso by calling the following:
val targetContext = InstrumentationRegistry.getTargetContext()
val launchIntent = Intent(targetContext, NameOfTheActivityYouAreTesting::class.java)
activityTestRule.finishActivity()
activityTestRule.launchActivity(launchIntent)
I actually wrote a helper function for this:
inline fun <reified T : Activity> ActivityTestRule<T>.restartActivity() {
finishActivity()
launchActivity(Intent(InstrumentationRegistry.getTargetContext(), T::class.java))
}
And I call it like this:
val activityTestRule = ActivityTestRule(ActivityIAmTesting::class.java)
#Test
fun someEspressoTest() {
// Some testing ...
// ...
activityTestRule.restartActivity()
// Some more testing...
// ...
}
You could use UIAutomator for this if you run your tests on an API level where the recent screen has application labels:
UiDevice device = UiDevice.getInstance(getInstrumentation());
Context appContext = getInstrumentation().getTargetContext();
String appName = appContext.getString(appContext.getApplicationInfo().labelRes);
device.findObject(new UiSelector().text(appName)).click();
Try UIAutomator. Tap recent apps twice to get back to your main application your Espresso is handling.
uiDevice.pressRecentApps()
Thread.sleep(1000)
uiDevice.pressRecentApps()
Thread.sleep(2000)

Programmatically execute Gatling tests

I want to use something like Cucumber JVM to drive performance tests written for Gatling.
Ideally the Cucumber features would somehow build a scenario dynamically - probably reusing predefined chain objects similar to the method described in the "Advanced Tutorial", e.g.
val scn = scenario("Scenario Name").exec(Search.search("foo"), Browse.browse, Edit.edit("foo", "bar")
I've looked at how the Maven plugin executes the scripts, and I've also seen mention of using an App trait but I can't find any documentation for the later and it strikes me that somebody else will have wanted to do this before...
Can anybody point (a Gatling noob) in the direction of some documentation or example code of how to achieve this?
EDIT 20150515
So to explain a little more:
I have created a trait which is intended to build up a sequence of, I think, ChainBuilders that are triggered by Cucumber steps:
trait GatlingDsl extends ScalaDsl with EN {
private val gatlingActions = new ArrayBuffer[GatlingBehaviour]
def withGatling(action: GatlingBehaviour): Unit = {
gatlingActions += action
}
}
A GatlingBehaviour would look something like:
object Google {
class Home extends GatlingBehaviour {
def execute: ChainBuilder =
exec(http("Google Home")
.get("/")
)
}
class Search extends GatlingBehaviour {...}
class FindResult extends GatlingBehaviour {...}
}
And inside the StepDef class:
class GoogleStepDefinitions extends GatlingDsl {
Given( """^the Google search page is displayed$""") { () =>
println("Loading www.google.com")
withGatling(Home())
}
When( """^I search for the term "(.*)"$""") { (searchTerm: String) =>
println("Searching for '" + searchTerm + "'...")
withGatling(Search(searchTerm))
}
Then( """^"(.*)" appears in the search results$""") { (expectedResult: String) =>
println("Found " + expectedResult)
withGatling(FindResult(expectedResult))
}
}
The idea being that I can then execute the whole sequence of actions via something like:
val scn = Scenario(cucumberScenario).exec(gatlingActions)
setup(scn.inject(atOnceUsers(1)).protocols(httpConf))
and then check the reports or catch an exception if the test fails, e.g. response time too long.
It seems that no matter how I use the 'exec' method it tries to instantly execute it there and then, not waiting for the scenario.
Also I don't know if this is the best approach to take, we'd like to build some reusable blocks for our Gatling tests that can be constructed via Cucumber's Given/When/Then style. Is there a better or already existing approach?
Sadly, it's not currently feasible to have Gatling directly start a Simulation instance.
Not that's it's not technically feasible, but you're just the first person to try to do this.
Currently, Gatling is usually in charge of compiling and can only be passed the name of the class to load, not an instance itself.
You can maybe start by forking io.gatling.app.Gatling and io.gatling.core.runner.Runner, and then provide a PR to support this new behavior. The former is the main entry point, and the latter the one can instanciate and run the simulation.
I recently ran into a similar situation, and did not want to fork gatling. And while this solved my immediate problem, it only partially solves what you are trying to do, but hopefully someone else will find this useful.
There is an alternative. Gatling is written in Java and Scala so you can call Gatling.main directly and pass it the arguments you need to run the Gatling Simulation you want. The problem is, the main explicitly calls System.exit so you have to also use a custom security manager to prevent it from actually exiting.
You need to know two things:
the class (with the full package) of the Simulation you want to run
example: com.package.your.Simulation1
the path where the binaries are compiled.
The code to run a Simulation:
protected void fire(String gatlingGun, String binaries){
SecurityManager sm = System.getSecurityManager();
System.setSecurityManager(new GatlingSecurityManager());
String[] args = {"--simulation", gatlingGun,
"--results-folder", "gatling-results",
"--binaries-folder", binaries};
try {
io.gatling.app.Gatling.main(args);
}catch(SecurityException se){
LOG.debug("gatling test finished.");
}
System.setSecurityManager(sm);
}
The simple security manager i used:
public class GatlingSecurityManager extends SecurityManager {
#Override
public void checkExit(int status){
throw new SecurityException("Tried to exit.");
}
#Override
public void checkPermission(Permission perm) {
return;
}
}
The problem is then getting the information you want out of the simulation after it has been run.