Mobile AGile

Godfrey Nolan

Agenda

  • Android (am)
  • iOS (pm)

ANDROID Agenda

  • Why??
  • Unit, UI and API testing 101
  • Calculator Example
  • More Tools - FIRST
  • ETA Detroit
  • jUnit Testing 
  • Espresso
  • Postman / Newman
  • Jenkins

iOs Agenda

  • Why??
  • Unit, UI and API testing 101
  • Calculator Example
  • No really why?? (FIRST)
  • ETA Detroit
  • XCTest  
  • XCUI 
  • Postman / Newman
  • Jenkins

FOLLOW ALONG

git clone http://github.com/godfreynolan/CodeCraftsman

WHY

  • Catch more mistakes
  • Confidently make more changes
  • Built in regression testing
  • Extend the life of your codebase

Unit Testing intro

public double add(double firstOperand, double secondOperand) {
    return firstOperand + secondOperand;
}


            @Test
            public void calculator_CorrectAdd_ReturnsTrue() {
                assertEquals(7, add(3,4);
            }

            @Test
            public void calculator_CorrectAdd_ReturnsTrue() {
                assertEquals("Addition is broken", 7, add(3,4);
            }
dependencies {
    // Unit testing dependencies.
    testCompile 'junit:junit:4.12'
}

UNIT TESTING 101

  • Command line
  • Setup and Teardown
  • Assertions
  • Parameters
  • Code Coverage
C:\Users\godfrey\AndroidStudioProjects\BasicSample>gradlew test --continue
Downloading https://services.gradle.org/distributions/gradle-2.2.1-all.zip
................................................................................
..................................................
Unzipping C:\Users\godfrey\.gradle\wrapper\dists\gradle-2.2.1-all\6dibv5rcnnqlfbq9klf8imrndn\gradle-2.2.1-all.zip to C:\Users\godfrey\.gradle\wrapper\dists\gradle-2.2.1-all\6dibv5rcnnqlfbq9klf8imrndn
Download https://jcenter.bintray.com/com/google/guava/guava/17.0/guava-17.0.jar
Download https://jcenter.bintray.com/com/android/tools/lint/lint-api/24.2.3/lint-api-24.2.3.jar
Download https://jcenter.bintray.com/org/ow2/asm/asm-analysis/5.0.3/asm-analysis-5.0.3.jar
Download https://jcenter.bintray.com/com/android/tools/external/lombok/lombok-ast/0.2.3/lombok-ast-0.2.3.jar
:app:preBuild UP-TO-DATE
:app:preDebugBuild UP-TO-DATE
:app:checkDebugManifest
:app:prepareDebugDependencies
:app:compileDebugAidl
:app:compileDebugRenderscript
.
.
.
:app:compileReleaseUnitTestSources
:app:assembleReleaseUnitTest
:app:testRelease
:app:test

BUILD SUCCESSFUL

Total time: 3 mins 57.013 secs
public class CalculatorTest {

    private Calculator mCalculator;

    @Before
    public void setUp() {
        mCalculator = new Calculator();
    }

    @Test
    public void calculator_CorrectAdd_ReturnsTrue() {
        double resultAdd = mCalculator.add(3, 4);
        assertEquals(7, resultAdd,0);
    }

    @After
    public void tearDown() {
        mCalculator = null;
    }
}

UNIT TESTING 101

  • assertEquals
  • assertTrue
  • assertFalse
  • assertNull
  • assertNotNull
  • assertSame
  • assertNotSame
  • assertThat
  • fail
@RunWith(Parameterized.class)
public class CalculatorParamTest {

    private int mOperandOne, mOperandTwo, mExpectedResult;
    private Calculator mCalculator;

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                {3, 4, 7}, {4, 3, 7}, {8, 2, 10}, {-1, 4, 3}, {3256, 4, 3260}
        });
    }

    public CalculatorParamTest(int mOperandOne, int mOperandTwo, int mExpectedResult) {
        this.mOperandOne = mOperandOne;
        this.mOperandTwo = mOperandTwo;
        this.mExpectedResult = mExpectedResult;
    }

    @Before
    public void setUp() { mCalculator = new Calculator(); }

    @Test
    public void testAdd_TwoNumbers() {
        int resultAdd = mCalculator.add(mOperandOne, mOperandTwo);
        assertEquals(mExpectedResult, resultAdd, 0);
    }
}

Espresso

  • GUI Testing
  • OnView
  • OnData
  • gradlew connectedCheck
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
 
  @Rule
  public ActivityTestRule<MainActivity> activityTestRule
    = new ActivityTestRule<> (MainActivity.class);
  @Test
  public void helloWorldTest() {
    onView(withId(R.id.hello_world))
      .check(matches(withText(R.string.hello_world)));
 
  }
}
@Test
public void helloWorldButtonTest(){
 
  onView(withId(R.id.button))
    .perform(click())
    .check(matches(isEnabled()));
 
}

MORE TOOLS - BUT WHY??

  • F(ast)
  • I(solated)
  • R(epeatable)
  • S(elf-verifying)
  • T(imely) i.e. TDD not TAD

MockiTO TEMPLATE

    @Test
    public void test() throws Exception {

        // Arrange, prepare behavior
        Helper aMock = mock(Helper.class);
        when(aMock.isCalled()).thenReturn(true);

        // Act
        testee.doSomething(aMock);

        // Assert - verify interactions
        verify(aMock).isCalled();
    
    }
    when(methodIsCalled).thenReturn(aValue);

TROUBLE SHOOTING

  • run gradlew build from the command line
  • Add sdk.dir to local.properties
  • sdk.dir=/home/godfrey/android/sdk

TEST DRIVEN DEVELOPMENT (TDD)

  • Unit testing vs TDD
  • Why TDD
  • Sample app
  • Lessons learned

TEST DRIVEN DEVELOPMENT 

  • Write test first
  •  See it fail
  •  Write simplest possible solution
    to get test to pass
  •  Refactor
  •  Wash, Rinse, Repeat 

 

TEST DRIVEN DEVELOPMENT 

  • Built in regression testing
  • Longer life for your codebase 
  • YAGNI feature development
  • Red/Green/Refactor helps
    kill procrastination

 

TDD

You can't TDD w/o unit testing

TDD means writing the tests before the code

TDD is more painless than classic unit testing

 

Unit TESTING

You can unit test w/o TDD

Unit tests don't mandate when you write the tests

Unit tests are often written at the end of a coding cycle

STEPS

  • Introduce Continuous Integration to build code
  • Configure android projects for TDD
  • Add minimal unit tests based on existing tests, add to CI
  • Show team how to create unit tests
  • Add testing code coverage metrics to CI, expect 5-10%
  • Add Espresso tests
  • Unit test new features or sprouts, mock existing objects
  • Wrap or ring fence existing code, remove unused code
  • Refactor wrapped code to get code coverage to 60-70%
    (New refactoring in Android Studio)

CONTACT INFO

godfrey@riis.com

 

iOs Agenda

  • Why??
  • Unit, UI and API testing 101
  • Calculator Example
  • No really why?? (FIRST)
  • ETA Detroit
  • XCTest  
  • XCUI 
  • Postman / Newman
  • Jenkins

WHY

  • Catch more mistakes
  • Confidently make more changes
  • Built in regression testing
  • Extend the life of your codebase

FOLLOW ALONG

git clone http://github.com/godfreynolan/CodeCraftsman

MORE TOOLS - BUT WHY??

  • F(ast)
  • I(solated)
  • R(epeatable)
  • S(elf-verifying)
  • T(imely) i.e. TDD not TAD

Tools OF THE TRADE

  • XCTest

  • Cuckoo (Mocking)

  • XCUI

  • Postman

  • Jenkins

Cuckoo

  • Add Cuckoo to the test target in Podfile

  • Pod install

  • Add Run Script to build phases

  • Add GeneratedMocks.swift file to tests

# Define output file; change "${PROJECT_NAME}Tests" to your test's root source folder, if it's not the default name
OUTPUT_FILE="./${PROJECT_NAME}Tests/GeneratedMocks.swift"
echo "Generated Mocks File = ${OUTPUT_FILE}"

# Define input directory; change "${PROJECT_NAME}" to your project's root source folder, if it's not the default name
INPUT_DIR="./${PROJECT_NAME}"
echo "Mocks Input Directory = ${INPUT_DIR}"

# Generate mock files; include as many input files as you'd like to create mocks for
${PODS_ROOT}/Cuckoo/run generate --testable "${PROJECT_NAME}" \
--output "${OUTPUT_FILE}" \
"${INPUT_DIR}/JSONFetcher.swift"

# ... and so forth

# After running once, locate `GeneratedMocks.swift` and drag it into your Xcode test target group

XCUI

  • User Interface testing

  • Two options

    ▪Recorded tests

    ▪Roll your own

XCUI has 3 Components

  • XCUIApplication

    • Launch the app

  • XCUIElementQuery

    • Find the XCUI Element to test

  • XCUIElement

    • Perform Test

CONTACT INFO

godfrey@riis.com

 

Mobile Agile

By godfreynolan

Mobile Agile

  • 734