SlideShare ist ein Scribd-Unternehmen logo
1 von 41
Android
 TDD &
Marcin Gryszko
 @mgryszko
New
gig
User stories
  written
Kickoff?
walking
     skeleton
“Implementation of the
thinnest possible slice of
real functionality that we
can automatically build,
deploy, and test end-to-
end”
original idea by Alistair Cockburn
Iteration “zero”
  TDD cycle
0
1. Understand the problem
2. Think about architecture
3. Automate
Android testing framework
Architecture
Automate
+ Android
  plugin
Failing acceptance test
public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {

    public void test_chirps_are_displayed_timely_ordered() throws Exception {
        timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size());
        assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps());
    }
}
Failing acceptance test
public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {

    public void test_chirps_are_displayed_timely_ordered() throws Exception {
        timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size());
        assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps());
    }
}
Page object
public class TimelineDriver {
    private Solo solo;
    public TimelineDriver(Solo solo) { this.solo = solo; }

    public void waitUntilTimelineLoaded(int timelineSize) {
        solo.waitForView(TableRow.class, timelineSize, 5000);
    }

    public List<Chirp> getDisplayedTimeline() {
        ListView timelineView = (ListView) solo.getView(R.id.timelineView);
        List<Chirp> chirps = new ArrayList<Chirp>();
        for (int i = 0; i < timelineView.getCount(); i++) {
            chirps.add((Chirp) timelineView.getItemAtPosition(i));
        }
        return chirps;
    }
}
Failing acceptance test
public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {

    private Solo solo;
    private TimelineDriver timelineDriver;

    protected void setUp() throws Exception {
        super.setUp();
        solo = new Solo(getInstrumentation(), getActivity());
        timelineDriver = new TimelineDriver(solo);
    }

    public void test_chirps_are_displayed_timely_ordered() throws Exception {
        timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size());
        assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps());
    }

    private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) {
        assertEquals(expected, actual);
    }
}
Passing acceptance test
public class TimelineActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {   // super.onCreate & setContentView omitted
        List<Chirp> chirps = loadTimeline();
        displayTimeline(chirps);
    }

    private List<Chirp> loadTimeline() {
        return executeJsonRequest("http://...", "mgryszko");
    }

    private List<Chirp> executeJsonRequest(String uri, Object... requestParams) {
        RestTemplate restTemplate = new RestTemplate();
        return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams));
    }

    private void displayTimeline(List<Chirp> chirps) {
        TimelineAdapter adapter = new TimelineAdapter();
        adapter.setActivity(this);
        adapter.setChirps(chirps);
        ListView timelineView = (ListView) findViewById(R.id.timelineView);
        timelineView.setAdapter(adapter);
    }
}
Tracer bullet
     hit
Let’s
 ref
 ac
 tor
Hamcrest
public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {

    private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) {
        assertEquals(expected, actual);
    }
}
Hamcrest
public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {

    private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) {
        assertThat(actual.size(), equalTo(expected.size()));
        for (Chirp chirp : expected) {
            assertThat(format("Chirp %s not in the timeline", chirp), actual, hasItem(chirp));
        }
    }
}
Roboguice
public class TimelineActivity extends Activity {
    private void displayTimeline(List<Chirp> chirps) {
        TimelineAdapter adapter = new TimelineAdapter();
        adapter.setActivity(this);
        adapter.setChirps(chirps);
        ListView timelineView = (ListView) findViewById(R.id.timelineView);
        timelineView.setAdapter(adapter);
    }
}
Roboguice
public class TimelineActivity extends RoboActivity {
    @InjectView(R.id.timelineView)
    private ListView timelineView;

    private void displayTimeline(List<Chirp> chirps) {
        TimelineAdapter adapter = getInjector()
            .getInstance(TimelineAdapter.class)
            .withChirps(chirps));
        timelineView.setAdapter(adapter);
    }
}
Falling integration test
public class ChirpJsonRepositoryTest extends TestCase {
    private ChirpRepository repository = new ChirpJsonRepository();

    public void test_returns_the_timeline_of_a_chirper() {
        assertTimelineEqualTo(repository.findTimelineOf("mgryszko"), mgryszkosChirps());
    }
}
Passing integration test
public class ChirpJsonRepository implements ChirpRepository {
    public List<Chirp> findTimelineOf(String chirper) {
        return executeJsonRequest("http://...", chirper);
    }

    private List<Chirp> executeJsonRequest(String uri, Object... requestParams) {
        RestTemplate restTemplate = new RestTemplate();
        return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams));
    }
}
Passing integration test
public class TimelineActivity extends Activity {
    private List<Chirp> loadTimeline() {
        return executeJsonRequest("http://...", "mgryszko");
    }

    private List<Chirp> executeJsonRequest(String uri, Object... requestParams) {
        RestTemplate restTemplate = new RestTemplate();
        return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams));
    }
}
Passing integration test
public class TimelineActivity extends RoboActivity {
    @Inject
    private ChirpRepository chirpRepository;

    private List<Chirp> loadTimeline() {
        return chirpRepository.findTimelineOf("mgryszko");
    }
}
Optimization
Async loading
public class TimelineActivity extends RoboActivity
    implements TimelineLoadListener {

    @Inject
    private AsyncTimelineLoader timelineLoader;

    private void loadTimeline() {
        timelineLoader.loadChirperTimeline("mgryszko", this);
    }

    public void timelineLoaded(List<Chirp> timeline) {
        displayTimeline(timeline);
    }
}

public interface TimelineLoadListener {
    void timelineLoading();

    void timelineLoaded(List<Chirp> timeline);
}
Async loading
public class TimelineLoadTask extends RoboAsyncTask<List<Chirp>>
    implements AsyncTimelineLoader {

    public void loadChirperTimeline(String chirper, TimelineLoadListener loadListener) {
        this.execute();
    }

    @Override
    protected void onPreExecute() throws Exception {
        loadListener.timelineLoading();
    }

    public List<Chirp> call() throws Exception {
        return repository.findTimelineOf(chirper);
    }

    @Override
    protected void onSuccess(List<Chirp> chirps) throws Exception {
        loadListener.timelineLoaded(chirps);
    }
}
First unit test
public class TimelineLoadTaskTest extends RoboUnitTestCase<Application> {

    private Mockery context = new Mockery();
    private ChirpRepository repository = context.mock(ChirpRepository.class);
    private TimelineLoadListener loadListener = context.mock(TimelineLoadListener.class);

    public void test_loads_timeline_successfully_and_notifies_the_listener() {
        TimelineLoadTask task = createTimelineLoadTask();

        context.checking(new Expectations() {{
            List<Chirp> timeline = asList(A_CHIRP);

               oneOf(loadListener).timelineLoading();
               oneOf(repository).findTimelineOf(CHIRPER); will(returnValue(timeline));
               oneOf(loadListener).timelineLoaded(with(timeline));
        }});

        executeInFakeUIThread(task);
    }
}
First unit test
    private CountDownLatch taskDone = new CountDownLatch(1);

    private TimelineLoadTask createTimelineLoadTask() {
        return new TimelineLoadTask(repository) {
            @Override
            protected void onFinally() {
                taskDone.countDown();
            }
        };
    }

    private void executeInFakeUIThread(final TimelineLoadTask task) {
        new RoboLooperThread() {
            public void run() {
                task.loadChirperTimeline(CHIRPER, loadListener);
            }
        }.start();
        taskDone.await();
    }
}
Really unit?
straight JUnit
IntelliJ
java.lang.RuntimeException: Stub!
at junit.runner.BaseTestRunner.(BaseTestRunner.java:5)




Maven
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024
sec
TestNG
IntelliJ
===============================================
Custom suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================



Maven
Running TestSuite
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.298
sec
Robolectric
@RunWith(InjectingTestRunner.class)
public class TimelineUnitTest {
    @Inject
    private TimelineActivity activity;

    @Inject
    private AsyncTimelineLoaderStub timelineLoader;

    @Test
    public void triggers_timeline_loading() {
        activity.onCreate(NO_SAVED_INSTANCE_STATE);
        assertThat(timelineLoader.isTimelineLoaded(), is(true));
    }

    @Test
    public void progress_dialog_is_displayed_when_timeline_is_loaded() {
        activity.timelineLoading();
        assertThat(Robolectric.shadowOf(activity).getLastShownDialogId(),
            is(TimelineActivity.PROGRESS_DIALOG_ID));
    }
}
Wrap-up
Thanks!
https://github.com/mgryszko/android-
                 tdd-ci
you
              ?
  jobs     Marcin
   @
osoco.es
Image sources:
http://www.flickr.com/photos/sarah_mccans/219287847/
http://www.flickr.com/photos/f-oxymoron/5005146417/
http://www.bluebison.net/content/2007/a-skeleton-walking-his-pets/
http://www.flickr.com/photos/familymwr/5009855774/
http://www.flickr.com/photos/zbraineater/2213219097/
http://www.flickr.com/photos/stusev/3296738594/
http://www.flickr.com/photos/hadamsky/293310259/
http://www.flickr.com/photos/qthomasbower/3392847831/
http://www.flickr.com/photos/mikecogh/5113779851/

Weitere ähnliche Inhalte

Was ist angesagt?

Exploring OpenFaaS autoscalability on Kubernetes with the Chaos Toolkit
Exploring OpenFaaS autoscalability on Kubernetes with the Chaos ToolkitExploring OpenFaaS autoscalability on Kubernetes with the Chaos Toolkit
Exploring OpenFaaS autoscalability on Kubernetes with the Chaos ToolkitSylvain Hellegouarch
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code EffectivelyAndres Almiray
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Andres Almiray
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xTatsuya Maki
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Andres Almiray
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleAnton Arhipov
 
JPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APIJPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APItvaleev
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistAnton Arhipov
 
Cassandra Summit EU 2014 - Testing Cassandra Applications
Cassandra Summit EU 2014 - Testing Cassandra ApplicationsCassandra Summit EU 2014 - Testing Cassandra Applications
Cassandra Summit EU 2014 - Testing Cassandra ApplicationsChristopher Batey
 
Adam Sitnik "State of the .NET Performance"
Adam Sitnik "State of the .NET Performance"Adam Sitnik "State of the .NET Performance"
Adam Sitnik "State of the .NET Performance"Yulia Tsisyk
 
RxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMixRxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMixTracy Lee
 
Software Testing - Invited Lecture at UNSW Sydney
Software Testing - Invited Lecture at UNSW SydneySoftware Testing - Invited Lecture at UNSW Sydney
Software Testing - Invited Lecture at UNSW Sydneyjulien.ponge
 
Testing time and concurrency Rx
Testing time and concurrency RxTesting time and concurrency Rx
Testing time and concurrency RxTamir Dresher
 
Akka.NET streams and reactive streams
Akka.NET streams and reactive streamsAkka.NET streams and reactive streams
Akka.NET streams and reactive streamsBartosz Sypytkowski
 
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"Yulia Tsisyk
 

Was ist angesagt? (20)

Exploring OpenFaaS autoscalability on Kubernetes with the Chaos Toolkit
Exploring OpenFaaS autoscalability on Kubernetes with the Chaos ToolkitExploring OpenFaaS autoscalability on Kubernetes with the Chaos Toolkit
Exploring OpenFaaS autoscalability on Kubernetes with the Chaos Toolkit
 
Testing Java Code Effectively
Testing Java Code EffectivelyTesting Java Code Effectively
Testing Java Code Effectively
 
Code Samples
Code SamplesCode Samples
Code Samples
 
Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
 
Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017Making the most of your gradle build - Gr8Conf 2017
Making the most of your gradle build - Gr8Conf 2017
 
Automated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.xAutomated%20testing%20with%20Espresso2.x
Automated%20testing%20with%20Espresso2.x
 
Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017Making the most of your gradle build - Greach 2017
Making the most of your gradle build - Greach 2017
 
GeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassleGeeCON 2017 - TestContainers. Integration testing without the hassle
GeeCON 2017 - TestContainers. Integration testing without the hassle
 
JPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APIJPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream API
 
JEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with JavassistJEEConf 2017 - Having fun with Javassist
JEEConf 2017 - Having fun with Javassist
 
Cassandra Summit EU 2014 - Testing Cassandra Applications
Cassandra Summit EU 2014 - Testing Cassandra ApplicationsCassandra Summit EU 2014 - Testing Cassandra Applications
Cassandra Summit EU 2014 - Testing Cassandra Applications
 
Adam Sitnik "State of the .NET Performance"
Adam Sitnik "State of the .NET Performance"Adam Sitnik "State of the .NET Performance"
Adam Sitnik "State of the .NET Performance"
 
RxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMixRxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMix
 
Software Testing - Invited Lecture at UNSW Sydney
Software Testing - Invited Lecture at UNSW SydneySoftware Testing - Invited Lecture at UNSW Sydney
Software Testing - Invited Lecture at UNSW Sydney
 
Easy Button
Easy ButtonEasy Button
Easy Button
 
V8
V8V8
V8
 
Testing time and concurrency Rx
Testing time and concurrency RxTesting time and concurrency Rx
Testing time and concurrency Rx
 
Testing with Kotlin
Testing with KotlinTesting with Kotlin
Testing with Kotlin
 
Akka.NET streams and reactive streams
Akka.NET streams and reactive streamsAkka.NET streams and reactive streams
Akka.NET streams and reactive streams
 
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"
Рахманов Александр "Что полезного в разборе дампов для .NET-разработчиков?"
 

Ähnlich wie Android TDD & CI

RxJava и Android. Плюсы, минусы, подводные камни
RxJava и Android. Плюсы, минусы, подводные камниRxJava и Android. Плюсы, минусы, подводные камни
RxJava и Android. Плюсы, минусы, подводные камниStfalcon Meetups
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good TestsTomek Kaczanowski
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on AndroidTomáš Kypta
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
 
Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Paco de la Cruz
 
Java agents are watching your ByteCode
Java agents are watching your ByteCodeJava agents are watching your ByteCode
Java agents are watching your ByteCodeRoman Tsypuk
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeDaniel Wellman
 
Sustaining Test-Driven Development
Sustaining Test-Driven DevelopmentSustaining Test-Driven Development
Sustaining Test-Driven DevelopmentAgileOnTheBeach
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unitliminescence
 
Trisha gee concurrentprogrammingusingthedisruptor
Trisha gee concurrentprogrammingusingthedisruptorTrisha gee concurrentprogrammingusingthedisruptor
Trisha gee concurrentprogrammingusingthedisruptorEthanTu
 
Retrofit
RetrofitRetrofit
Retrofitbresiu
 
C#을 이용한 task 병렬화와 비동기 패턴
C#을 이용한 task 병렬화와 비동기 패턴C#을 이용한 task 병렬화와 비동기 패턴
C#을 이용한 task 병렬화와 비동기 패턴명신 김
 
Protocol-Oriented Networking
Protocol-Oriented NetworkingProtocol-Oriented Networking
Protocol-Oriented NetworkingMostafa Amer
 

Ähnlich wie Android TDD & CI (20)

Anti patterns
Anti patternsAnti patterns
Anti patterns
 
RxJava и Android. Плюсы, минусы, подводные камни
RxJava и Android. Плюсы, минусы, подводные камниRxJava и Android. Плюсы, минусы, подводные камни
RxJava и Android. Плюсы, минусы, подводные камни
 
Rx workshop
Rx workshopRx workshop
Rx workshop
 
Jason parsing
Jason parsingJason parsing
Jason parsing
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on Android
 
Fault tolerance made easy
Fault tolerance made easyFault tolerance made easy
Fault tolerance made easy
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Jersey Guice AOP
Jersey Guice AOPJersey Guice AOP
Jersey Guice AOP
 
Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)Durable functions 2.0 (2019-10-10)
Durable functions 2.0 (2019-10-10)
 
Java agents are watching your ByteCode
Java agents are watching your ByteCodeJava agents are watching your ByteCode
Java agents are watching your ByteCode
 
How to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy CodeHow to Start Test-Driven Development in Legacy Code
How to Start Test-Driven Development in Legacy Code
 
Sustaining Test-Driven Development
Sustaining Test-Driven DevelopmentSustaining Test-Driven Development
Sustaining Test-Driven Development
 
Pragmatic unittestingwithj unit
Pragmatic unittestingwithj unitPragmatic unittestingwithj unit
Pragmatic unittestingwithj unit
 
JDK Power Tools
JDK Power ToolsJDK Power Tools
JDK Power Tools
 
Trisha gee concurrentprogrammingusingthedisruptor
Trisha gee concurrentprogrammingusingthedisruptorTrisha gee concurrentprogrammingusingthedisruptor
Trisha gee concurrentprogrammingusingthedisruptor
 
Retrofit
RetrofitRetrofit
Retrofit
 
C#을 이용한 task 병렬화와 비동기 패턴
C#을 이용한 task 병렬화와 비동기 패턴C#을 이용한 task 병렬화와 비동기 패턴
C#을 이용한 task 병렬화와 비동기 패턴
 
Java programs
Java programsJava programs
Java programs
 
Protocol-Oriented Networking
Protocol-Oriented NetworkingProtocol-Oriented Networking
Protocol-Oriented Networking
 

Kürzlich hochgeladen

Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 

Kürzlich hochgeladen (20)

Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 

Android TDD & CI

  • 1. Android TDD & Marcin Gryszko @mgryszko
  • 3. User stories written
  • 5. walking skeleton “Implementation of the thinnest possible slice of real functionality that we can automatically build, deploy, and test end-to- end” original idea by Alistair Cockburn
  • 7. 0 1. Understand the problem 2. Think about architecture 3. Automate
  • 11. + Android plugin
  • 12.
  • 13. Failing acceptance test public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); } }
  • 14. Failing acceptance test public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); } }
  • 15. Page object public class TimelineDriver { private Solo solo; public TimelineDriver(Solo solo) { this.solo = solo; } public void waitUntilTimelineLoaded(int timelineSize) { solo.waitForView(TableRow.class, timelineSize, 5000); } public List<Chirp> getDisplayedTimeline() { ListView timelineView = (ListView) solo.getView(R.id.timelineView); List<Chirp> chirps = new ArrayList<Chirp>(); for (int i = 0; i < timelineView.getCount(); i++) { chirps.add((Chirp) timelineView.getItemAtPosition(i)); } return chirps; } }
  • 16. Failing acceptance test public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private Solo solo; private TimelineDriver timelineDriver; protected void setUp() throws Exception { super.setUp(); solo = new Solo(getInstrumentation(), getActivity()); timelineDriver = new TimelineDriver(solo); } public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); } private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertEquals(expected, actual); } }
  • 17. Passing acceptance test public class TimelineActivity extends Activity { public void onCreate(Bundle savedInstanceState) { // super.onCreate & setContentView omitted List<Chirp> chirps = loadTimeline(); displayTimeline(chirps); } private List<Chirp> loadTimeline() { return executeJsonRequest("http://...", "mgryszko"); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); } private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = new TimelineAdapter(); adapter.setActivity(this); adapter.setChirps(chirps); ListView timelineView = (ListView) findViewById(R.id.timelineView); timelineView.setAdapter(adapter); } }
  • 20. Hamcrest public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertEquals(expected, actual); } }
  • 21. Hamcrest public class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertThat(actual.size(), equalTo(expected.size())); for (Chirp chirp : expected) { assertThat(format("Chirp %s not in the timeline", chirp), actual, hasItem(chirp)); } } }
  • 22. Roboguice public class TimelineActivity extends Activity { private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = new TimelineAdapter(); adapter.setActivity(this); adapter.setChirps(chirps); ListView timelineView = (ListView) findViewById(R.id.timelineView); timelineView.setAdapter(adapter); } }
  • 23. Roboguice public class TimelineActivity extends RoboActivity { @InjectView(R.id.timelineView) private ListView timelineView; private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = getInjector() .getInstance(TimelineAdapter.class) .withChirps(chirps)); timelineView.setAdapter(adapter); } }
  • 24. Falling integration test public class ChirpJsonRepositoryTest extends TestCase { private ChirpRepository repository = new ChirpJsonRepository(); public void test_returns_the_timeline_of_a_chirper() { assertTimelineEqualTo(repository.findTimelineOf("mgryszko"), mgryszkosChirps()); } }
  • 25. Passing integration test public class ChirpJsonRepository implements ChirpRepository { public List<Chirp> findTimelineOf(String chirper) { return executeJsonRequest("http://...", chirper); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); } }
  • 26. Passing integration test public class TimelineActivity extends Activity { private List<Chirp> loadTimeline() { return executeJsonRequest("http://...", "mgryszko"); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); } }
  • 27. Passing integration test public class TimelineActivity extends RoboActivity { @Inject private ChirpRepository chirpRepository; private List<Chirp> loadTimeline() { return chirpRepository.findTimelineOf("mgryszko"); } }
  • 29. Async loading public class TimelineActivity extends RoboActivity implements TimelineLoadListener { @Inject private AsyncTimelineLoader timelineLoader; private void loadTimeline() { timelineLoader.loadChirperTimeline("mgryszko", this); } public void timelineLoaded(List<Chirp> timeline) { displayTimeline(timeline); } } public interface TimelineLoadListener { void timelineLoading(); void timelineLoaded(List<Chirp> timeline); }
  • 30. Async loading public class TimelineLoadTask extends RoboAsyncTask<List<Chirp>> implements AsyncTimelineLoader { public void loadChirperTimeline(String chirper, TimelineLoadListener loadListener) { this.execute(); } @Override protected void onPreExecute() throws Exception { loadListener.timelineLoading(); } public List<Chirp> call() throws Exception { return repository.findTimelineOf(chirper); } @Override protected void onSuccess(List<Chirp> chirps) throws Exception { loadListener.timelineLoaded(chirps); } }
  • 31. First unit test public class TimelineLoadTaskTest extends RoboUnitTestCase<Application> { private Mockery context = new Mockery(); private ChirpRepository repository = context.mock(ChirpRepository.class); private TimelineLoadListener loadListener = context.mock(TimelineLoadListener.class); public void test_loads_timeline_successfully_and_notifies_the_listener() { TimelineLoadTask task = createTimelineLoadTask(); context.checking(new Expectations() {{ List<Chirp> timeline = asList(A_CHIRP); oneOf(loadListener).timelineLoading(); oneOf(repository).findTimelineOf(CHIRPER); will(returnValue(timeline)); oneOf(loadListener).timelineLoaded(with(timeline)); }}); executeInFakeUIThread(task); } }
  • 32. First unit test private CountDownLatch taskDone = new CountDownLatch(1); private TimelineLoadTask createTimelineLoadTask() { return new TimelineLoadTask(repository) { @Override protected void onFinally() { taskDone.countDown(); } }; } private void executeInFakeUIThread(final TimelineLoadTask task) { new RoboLooperThread() { public void run() { task.loadChirperTimeline(CHIRPER, loadListener); } }.start(); taskDone.await(); } }
  • 34. straight JUnit IntelliJ java.lang.RuntimeException: Stub! at junit.runner.BaseTestRunner.(BaseTestRunner.java:5) Maven Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 sec
  • 35. TestNG IntelliJ =============================================== Custom suite Total tests run: 1, Failures: 0, Skips: 0 =============================================== Maven Running TestSuite Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.298 sec
  • 36. Robolectric @RunWith(InjectingTestRunner.class) public class TimelineUnitTest { @Inject private TimelineActivity activity; @Inject private AsyncTimelineLoaderStub timelineLoader; @Test public void triggers_timeline_loading() { activity.onCreate(NO_SAVED_INSTANCE_STATE); assertThat(timelineLoader.isTimelineLoaded(), is(true)); } @Test public void progress_dialog_is_displayed_when_timeline_is_loaded() { activity.timelineLoading(); assertThat(Robolectric.shadowOf(activity).getLastShownDialogId(), is(TimelineActivity.PROGRESS_DIALOG_ID)); } }
  • 40. you ? jobs Marcin @ osoco.es

Hinweis der Redaktion

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n