Showing posts with label testing. Show all posts
Showing posts with label testing. Show all posts

07 November 2020

Android Hilt example and a unit test (with Robolectric)

Android Hilt has been released and I must say I'm quite impressed. I absolutely hate Dagger which I think comprises of far too much boiler plate code, its badly documented and incredibly confusing. Hilt is of course built on top of dagger and I think this is a huge win for the Android community. Hilt decreases confusion, increases code generation and is nice and simple.

I don't want to go into what Dependency Injection (DI) is, that's been done to death.

There are two resources I found really useful to get started with Hilt:

The Hilt documentation - https://dagger.dev/hilt/

Coding with Mitch videos: https://www.youtube.com/watch?v=zTpM2olXCok

There are two things I want to talk about in this blog post:

  1. A really quick intro and setup of a Hilt module.
  2. A unit test using Robolectic.
Before I go much further I would discourage you from using Robolectric. It's become a real pain to work with, requiring Java 9 for its latest version and regularly deprecating things without clear documentation on how to migrate. That said I've been using Robolectic for a while so I'm stuck with it until I can remove it from my code. I'm hoping Hilt and ViewModels will help me do this.

The first thing I wanted to look at is (I think) a fairly standard use case where a class has some dependencies and we want to use that class in our activity without creating it every time.

Step 1 - The application


@HiltAndroidApp
class MyApplication : Application()

Step 2 - The Class


class Petrol(val input: String) {

    fun getTheInfo(): String {
        return input
    }
}

Step 3 - The Module

Now this is the interesting bit, here we define a "factory" where we list how the class is to be created. Obviously this is a very simple example, but I think that's a good way to start.

@InstallIn(ApplicationComponent::class)
@Module
class PetrolFactory {

    @Provides
    fun getPetrol(): Petrol {
        return Petrol("Petroll")
    }
}

Step 4 - The Activity


Now we setup the activity and you'll note how little code we need to write to create and start using the class

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var petrol: Petrol

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(findViewById(R.id.toolbar))
        
        findViewById<TextView>(R.id.main_text).text = petrol.getTheInfo()
    }
}

I think this is fantastic and hopefully it's clear to see how easy this is and how quick you can make complex interactions really simple.

Step 5 - The Tests


With the tests I want to show how you would pass in something different for the purpose of testing. 

@UninstallModules(PetrolFactory::class)
@HiltAndroidTest
class MainActivityTest : RobolectricTestConfig() {

    @get:Rule
    var hiltRule = HiltAndroidRule(this)

    @Module
    @InstallIn(ApplicationComponent::class)
    class TestEngine {
        @Provides
        fun getPetrol(): Petrol {
            return Petrol("TestPetrol")
        }
    }


    @Before
    fun init() {
        hiltRule.inject()
    }

    @Test
    fun testActivityMainText() {
        val scenario = ActivityScenario.launch(MainActivity::class.java)

        scenario.onActivity {
            assertEquals("TestPetrol", it.findViewById<TextView>(R.id.main_text).text)
        }
    }
}


One thing that's really important to remember is you can't run the test with the green arrow in Android Studio. You need to use gradle. So just run the gradle task instead. That's it I hope that helps demonstrate how you would create a DI app with Hilt in a few easy steps. I also hope you can see how easy that makes testing.










15 June 2019

Mockito 2.0 and a thousand failing unit tests

Wow this killed me for a few days. I upgraded my long since out of date Mockito to the latest version 2.28.2 from version 1.something. Instantly 90% of my unit tests failed. Queue a long drawn out investigation to try and figure out what was happening. Numerous culprits were held under the spotlight and shaken down.

Ultimately, (as is usually the case) the answer was somewhat simple. My mock methods had all been declared irrelevant by this change to Mockito:

anyString() no longer accepts nulls.

So a mock method like this:

when(mockClass.mockMethod(anyString())).thenReturn("All your base are belong to me")

Simply stopped returning anything.

https://github.com/mockito/mockito/issues/185

The workaround is either to use any() or a deliberate null.

when(mockClass.mockMethod(any())).thenReturn("All your base are belong to me")


when(mockClass.mockMethod(isNull())).thenReturn("All your base are belong to me")


I hope this helps someone avoid my mistakes. Happy coding.

28 February 2014

Android Unit Tests


At my old gig I did unit tests....lots and lots of unit tests. At the mercy of one boisterous kiwi I got pretty good at it too, truth be told I began to really enjoy it. Unit testing (done well) gives you confidence in your code and allows you to make changes reassured that previous functionality won't break.
IMHO Any programmer worth their salt should unit test where possible.

Anyway, I've been in the Android world for some time and haven't really had the chance to start experimenting with unit testing. Thankfully Google has a really good explanation of how to set-up a unit testing environment:

http://developer.android.com/tools/testing/testing_android.html

However there's tonnes of information thrown at you there and their Eclipse example focuses on the UI. Now in my experience UI testing with automated tests is tricky. While it's fantastic if you can do it, I've found the UI changes too frequently too maintain automated tests. Business cases are naturally more concerned with the UI so it changes regularly and more often that not their are just too many factors involved. So I've written a very quick demo that focuses just on unit testing code.

The Android App To Be Tested

For the purposes of example I've created a very very simple Android app. Just run through the wizard and create a default app. I called mine myAwesomeAndroidApp. Then add the following method too the MainActivity:

public static boolean validateIsMonth(int month){
    
    if(month > 0 && month < 13){
        return true;
    }
    
    return false;
}

This is the method I want to test, simplest thing I could think of.

The Test Framework.

Eclipse ADT contains a framework for creating stand alone testing apps. This enables you to keep your tests and your deployable app separate. To start a new app goto
New -> Other -> Android Test Project


Follow the wizard through, of course ensuring you select your myAwesomeAndroidApp as the test target. You should now have two projects, the test project having an empty package.
In the empty package create a new class, call it TestValidation.java

Now paste the code in as such:

package com.example.myawesomeandroidapp.test;

import android.test.AndroidTestCase;

public class TestValidation extends AndroidTestCase {

    protected void setUp() throws Exception {
        super.setUp();
    }
    
}


Note the use of extends AndroidTestCase. This is very important and allows us to test Android projects.

Now add the unit tests, again this is a very basic example and you should make this more complicated as neccessary to suit your project and expectations.

    public void testValidation(){
        assertTrue(MainActivity.validateIsMonth(2));
    }
    
    public void testValidationFail(){
        assertFalse(MainActivity.validateIsMonth(13));
    }

You'll see we are testing our validateIsMonth method in the MainActivity, we're testing a pass case and a fail case. Again basic basic unit tests here but hopefully you get the idea.

If you right click on your project and click Run as -> Android JUnit Test your project should run and Eclipse should switch to JUnit view and show you a beautiful column of green lights indicating your unit tests have all passed :)



Hope this helps and happy testing.

Oh and jokes aside Adam Cameron has done some really awesome work on CFML unit testing, check out his blog for some great examples: http://cfmlblog.adamcameron.me/search/label/Unit%20Testing