How to create a Page/Screen Object Model in Jetpack Compose Testing - android-jetpack-compose

For basic testing, if I create a test class like below, it works fine.
class MyComposeTest {
#get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
#Test
fun myTest() {
composeTestRule.onNodeWithText("Login").performClick()
composeTestRule.onNodeWithText("Home").assertIsDisplayed()
}
}
But what if i want to abstract some of these into separate classes for an end-to-end test?
e.g. I want to create a login page class with all locators for Login and similarly for Home page and simplify my test as
#Test
fun myTest() {
val login = LoginPage()
val home = HomePage()
login.loginBtn.performClick()
home.homeTxt.assertIsDisplayed()
}
I am not sure how my page classes (with locators) should look like to make this possible.

You should pass the composeTestRule in the page's constructor. The code would look like this:
class BaseTestSuite {
#get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
}
class LoginPage(composeTestRule: ComposeContentTestRule) {
val loginBtn = onNodeWithText("Login")
fun tapLoginButton() {
loginBtn.performClick()
}
}
class MyTestSuite() : BaseTestSuite {
val loginPage = LoginPage(composeTestRule)
#Test
fun myTest() {
loginPage.tapLoginButton()
// rest of the code
}
}

Related

Instrument testing with Jetpack compose and Kotlin room

I'm testing several behaviours including:
FAB is hidden when table (Kotlin room) is empty
FAB navigates to a page when table (Kotlin room) is not empty
My approach is based on instrumented testing of jetpack but I'm unsure as to how to amend the database to simulate an empty/non-empty table needed to test the above behaviour.
But I'm not entirely sure if this is an ideal approach, and like to get your thoughts
#ExperimentalMaterial3Api
#HiltAndroidTest
#RunWith(AndroidJUnit4::class)
class HomeScreenTest {
#get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
#ExperimentalMaterial3Api
#get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<MainActivity>()
#Inject
lateinit var repoMoto: MotorcycleRepository
lateinit var repoFix: FixRepository
private lateinit var navController: NavHostController
private lateinit var viewModel: ViewModel
#ExperimentalMaterial3Api
#Before
fun setUp() {
hiltRule.inject()
composeTestRule.setContent {
viewModel = hiltViewModel()
navController = rememberNavController()
MotoAkuTheme {
BottomNavigation(
navController = navController,
vm = viewModel
)
}
}
}
#Test
fun When_noMotoInDb_expect_FABNotShown() {
// TODO Simulate empty Motorcycle table
// Verify FAB is not shown
composeTestRule.onNodeWithTag(BOTTOMNAV_FAB).assertDoesNotExist()
}
#Test
fun When_motoInDBAndfixFABPressed_expect_navigateToAddFixScreen() {
// TODO Simulate a non empty moto list
// Click fix FAB
composeTestRule.onNodeWithTag(BOTTOMNAV_FAB).performClick()
// Determine if route is navigated to Fix screen
val route = navController.currentBackStackEntry?.destination?.route
Assert.assertEquals(Screen.AddFix.name+"/?motoId={motoId}",route)
}
}
Usually, I approach my test as if I'd use the app physically. Hence, if I were to test behaviour that requires an empty/non-empty database, then I'd add then delete accordingly to simulate that.

Spock : Verify Interaction Not working -- Too few Invocations

I have a very simple class as shown
class MyClass {
public static String getName(String input)
{
return toUpperCase(input);
}
public static String toUpperCase(String name)
{
return name.toUpperCase();
}
}
To test the above I have written a Test Case using Spock FW as shown below:
class MyClassTest extends Specification{
def 'check uppercase scnario'() {
given:
MyClass myClass = new MyClass();
when:
myClass.getName("test")
then: ""
1*myClass.toUpperCase("test");
}
}
But when I run this I see 0 Interactions
You cannot verify interactions on static Java methods. Just make your methods non-static. Besides, in the given: block, you are instantiating the class, so I guess you want to use instance methods anyway.
class MyClass {
public String getName(String input) {
return toUpperCase(input);
}
public String toUpperCase(String name) {
return name.toUpperCase();
}
}
Furthermore, if you wish to verify interactions on self-invocation calls, a Mock() is not what you need. Instead, simply use a Spy():
class MyClassTest extends Specification {
def 'check uppercase scnario'() {
given:
MyClass myClass = Spy()
when:
myClass.getName("test")
then:
1 * myClass.toUpperCase("test");
}
}
Try it in the Groovy Web Console.

Groovy Meta Programming

I want to override a method definition in Grails. I am trying to use Groovy metaprogramming as the class which I want to override belongs to a framework.
Below is the original class.
class SpringSocialSimpleSignInAdapter implements SignInAdapter {
private RequestCache requestCache
SpringSocialSimpleSignInAdapter(RequestCache requestCache) {
this.requestCache = requestCache;
}
String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
SignInUtils.signin localUserId
extractOriginalUrl request
}
}
I am trying to override like below
SpringSocialSimpleSignInAdapter.metaClass.signIn = {java.lang.String str, org.springframework.social.connect.Connection conn, org.springframework.web.context.request.NativeWebRequest webreq ->
println 'coming here....' // my implementation here
return 'something'
}
But for some reason overriding is not hapening. I am not able to figure it out. Any help would be greatly appretiated.
Thanks
Yeah, seems like that bug. I don't know your whole scenario, but anyway, here's a small workaround i made:
In your class definition, you don't implement the interface
You create your object and do your metamagic
Use groovy coercion to make it act as the interface and then you can pass it around
Here is a small script i made using JIRA bug to prove it:
interface I {
def doIt()
}
class T /*implements I*/ {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
// here the coercion happens and the assertion works fine
def i = t as I
assert !i.doIt()
assert !t.doIt()
// here the polymorphism happens fine
def iOnlyAcceptInterface(I i) { assert !i.doIt() }
iOnlyAcceptInterface(i)

External function definition to validate in Domain Class

My validation looks like:
static constraints =
{
someProperty validator: { val, obj ->
// a lot of code here
}
}
How can I define external function which will pass to this validation (val, obj requierd) ?
Now my code isn't clear in constraints closures... there's too much validation code for someProperty.
How can I change it?
By creating a groovy class in the src/groovy directory, like :
public class CustomValidators {
static validateMe = { val, obj ->
// a dummy example...
return val < 1
}
}
Then, on your domain class use it like below :
static constraints =
{
someProperty validator: CustomValidators.validateMe
}

binding and closures groovy

I don't know how to use binding with closures in Groovy. I wrote a test code and while running it, it said, missing method setBinding on the closure passed as parameter.
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = {
assertEquals("apple", a)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
This works for me with Groovy 1.7.3:
someClosure = {
assert "apple" == a
}
void testMeasurement() {
prepareData(someClosure)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
testMeasurement()
In this script example, the setBinding call is setting a in the scripts binding (as you can see from the Closure documentation, there is no setBinding call). So after the setBinding call, you can call
println a
and it will print out "apple"
So to do this in the class, you can set the delegate for the closure (the closure will revert back to this delegate when a property cannot be found locally) like so:
class TestClass {
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = { ->
assert "apple" == a
}
void prepareData( testCase ) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.delegate = binding
testCase.call()
}
}
And it should grab the value fro a from the delegate class (in this case, a binding)
This page here goes through the usage of delegate and the scope of variables in Closures
Indeed, instead of using a Binding object, you should be able to use a simple Map like so:
void prepareData( testCase ) {
testCase.delegate = [ a:'apple' ]
testCase.call()
}
Hope it helps!
This is really strange behaviour: removing the "def" in front of someClosure declaration makes the script work in JDK1.6 Groovy:1.7.3
Update: This was posted in the answer above. My mistake to repeat it.
Update: Why it works? Without a def first line is taken as a property assignment which calls setProperty and makes the variable available in binding, which is resolved later.
a def should have worked as well as per (http://docs.codehaus.org/display/GROOVY/Groovy+Beans)
someClosure = {
assert "apple", a
print "Done"
}
void testMeasurement() {
prepareData(someClosure)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
testCase.setBinding(binding)
testCase.call()
}
testMeasurement()
I could reproduce the problem you mention by following code. But i am not sure if this is the correct way to use Binding. GroovyDocs says they are to be used with scripts. Could you point me to documentation which suggests such usage of Binding with Closures.
class TestBinding extends GroovyTestCase {
void testMeasurement() {
prepareData(someClosure)
}
def someClosure = {
assertEquals("apple", a)
}
void prepareData(testCase) {
def binding = new Binding()
binding.setVariable("a", "apple")
//this.setBinding(binding)
testCase.setBinding(binding)
testCase.call()
}
}
This was answered on groovy mailing list:
In a script, def foo will create a local variable, not a property
(private field + getter/setter).
Think of a script a bit like if it's the body of a run() or main() method.
That's where and how you can define local variables.

Resources