diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index d89bb99d65148fc964687815b11160c69ede5121..376628a3f2b171a13356e0cf701344c994d1dd78 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
diff --git a/.travis.yml b/.travis.yml
index 705321668533cf82bb1b7abe9c9c71782112b64e..7f4a898138b8015f0cbcbe071cf38b721f9d1a20 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ android:
     - build-tools-26.0.2
     - build-tools-27.0.3
     - android-22
-    - android-26
+    - android-27
     - add-on
     - extra
     - platform-tools
@@ -12,7 +12,7 @@ android:
     - extra-google-googleplayservices
     - extra-google-m2repository
     - extra-android-m2repository
-    - addon-google_apis-google-26
+    - addon-google_apis-google-27
     - sys-img-armeabi-v7a-android-22
 env:
   global:
@@ -21,7 +21,7 @@ env:
 sudo: required
 
 before_install:
-  - yes | sdkmanager "platforms;android-26"
+  - yes | sdkmanager "platforms;android-27"
 
 before_script:
   - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
diff --git a/app/build.gradle b/app/build.gradle
index 87c3accf67628aad5f9194b351f1126065191cc5..4a0020d2feeed75159f263c74a8c6ca33c7d6ba6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,11 +1,11 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 26
+    compileSdkVersion 27
     defaultConfig {
         applicationId "me.szaki.xkcd.xkcdbrowser"
         minSdkVersion 22
-        targetSdkVersion 26
+        targetSdkVersion 27
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -55,13 +55,13 @@ ext {
 
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
-    implementation 'com.android.support:appcompat-v7:26.1.0'
-    implementation 'com.android.support:support-v4:26.1.0'
-    implementation 'com.android.support:design:26.1.0'
+    implementation 'com.android.support:appcompat-v7:27.1.1'
+    implementation 'com.android.support:support-v4:27.1.1'
+    implementation 'com.android.support:design:27.1.1'
     implementation 'com.android.support.constraint:constraint-layout:1.1.0'
     testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'com.android.support.test:runner:1.0.1'
-    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
+    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
 
     // Dagger
     implementation 'com.google.dagger:dagger:2.11'
@@ -86,4 +86,13 @@ dependencies {
 
     // AndroidSwipeLayout
     implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
+
+    //Unit Test - Robolectric
+    //JUnit - JUnit 5 is not supported yet
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.robolectric:robolectric:3.7.1'
+    testImplementation 'org.mockito:mockito-core:2.15.0'
+    testImplementation 'com.google.dagger:dagger:2.11'
+    testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.11'
+    testCompileOnly 'javax.annotation:jsr250-api:1.0'
 }
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/ExampleUnitTest.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/ExampleUnitTest.java
deleted file mode 100644
index eeb658e7e6e8b32afcfae5727aa5660802bbc33f..0000000000000000000000000000000000000000
--- a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package me.szaki.xkcd.xkcdbrowser;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
-    @Test
-    public void addition_isCorrect() throws Exception {
-        assertEquals(4, 2 + 2);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/TestHelper.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/TestHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a705e61b7ef52566112d22595e3d237500a9cf46
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/TestHelper.java
@@ -0,0 +1,20 @@
+package me.szaki.xkcd.xkcdbrowser;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowLog;
+
+import java.util.List;
+
+import me.szaki.xkcd.xkcdbrowser.mock.dagger.DaggerMockComponent;
+import me.szaki.xkcd.xkcdbrowser.ui.UIModule;
+
+public class TestHelper {
+    public static void setTestInjector() {
+        ShadowLog.stream = System.out;
+        XKCDBrowserApplication application = (XKCDBrowserApplication) RuntimeEnvironment.application;
+        XKCDBrowserApplicationComponent injector = DaggerMockComponent.builder()
+                .uIModule(new UIModule(application.getApplicationContext()))
+                .build();
+        application.injector = injector;
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/api/MockAPI.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/api/MockAPI.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdae20c7426760ff9b9e575c0260cd41ad1feba2
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/api/MockAPI.java
@@ -0,0 +1,119 @@
+package me.szaki.xkcd.xkcdbrowser.mock.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import me.szaki.xkcd.xkcdbrowser.network.api.ComicApi;
+import me.szaki.xkcd.xkcdbrowser.network.model.Comic;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+public class MockAPI implements ComicApi {
+    @Override
+    public Call<Comic> getCurrentComic() {
+        final Comic currentComic = new Comic(){{
+            setImg("https://cataas.com/c");
+            setDay("4");
+            setMonth("4");
+            setNum(10);
+            setTitle("Title");
+            setYear("2000");
+        }};
+
+        Call<Comic> call = new Call<Comic>() {
+            @Override
+            public Response<Comic> execute() throws IOException {
+                return Response.success(currentComic);
+            }
+
+            @Override
+            public void enqueue(Callback<Comic> callback) {
+
+            }
+
+            @Override
+            public boolean isExecuted() {
+                return false;
+            }
+
+            @Override
+            public void cancel() {
+
+            }
+
+            @Override
+            public boolean isCanceled() {
+                return false;
+            }
+
+            @Override
+            public Call<Comic> clone() {
+                return null;
+            }
+        };
+
+        return call;
+    }
+
+    @Override
+    public Call<Comic> getComic(Long id) {
+        Comic comic = null;
+        if (10L == id) {
+            comic = new Comic() {{
+                setImg("https://cataas.com/c");
+                setDay("4");
+                setMonth("4");
+                setNum(10);
+                setTitle("Title");
+                setYear("2000");
+            }};
+
+        } else if (9L == id) {
+            comic = new Comic() {{
+                setImg("https://cataas.com/cat");
+                setDay("1");
+                setMonth("1");
+                setNum(9);
+                setTitle("Tittle");
+                setYear("1999");
+            }};
+        }
+
+        final Comic finalComic = comic;
+        Call<Comic> call = new Call<Comic>() {
+            @Override
+            public Response<Comic> execute() throws IOException {
+                return Response.success(finalComic);
+            }
+
+            @Override
+            public void enqueue(Callback<Comic> callback) {
+
+            }
+
+            @Override
+            public boolean isExecuted() {
+                return false;
+            }
+
+            @Override
+            public void cancel() {
+
+            }
+
+            @Override
+            public boolean isCanceled() {
+                return false;
+            }
+
+            @Override
+            public Call<Comic> clone() {
+                return null;
+            }
+        };
+
+        return call;
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockComponent.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..57dd10f4415b087b673ab7f009f522667473abb4
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockComponent.java
@@ -0,0 +1,13 @@
+package me.szaki.xkcd.xkcdbrowser.mock.dagger;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+import me.szaki.xkcd.xkcdbrowser.XKCDBrowserApplicationComponent;
+import me.szaki.xkcd.xkcdbrowser.interactor.InteractorModule;
+import me.szaki.xkcd.xkcdbrowser.ui.UIModule;
+
+@Singleton
+@Component(modules = {UIModule.class, InteractorModule.class, MockNetworkModule.class, MockDBModule.class})
+public interface MockComponent extends XKCDBrowserApplicationComponent {
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockDBModule.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockDBModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac4dafc83f3ebb0b225427ee40e05846e5785896
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockDBModule.java
@@ -0,0 +1,17 @@
+package me.szaki.xkcd.xkcdbrowser.mock.dagger;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import me.szaki.xkcd.xkcdbrowser.database.ComicsDatabase;
+import me.szaki.xkcd.xkcdbrowser.mock.db.MockComicDB;
+
+@Module
+public class MockDBModule {
+    @Provides
+    @Singleton
+    public ComicsDatabase provideComicsDatabase() {
+        return new MockComicDB();
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockNetworkModule.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockNetworkModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7eccc87ed1e211a443ecf8c518905fd9a142c85
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/dagger/MockNetworkModule.java
@@ -0,0 +1,17 @@
+package me.szaki.xkcd.xkcdbrowser.mock.dagger;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import me.szaki.xkcd.xkcdbrowser.mock.api.MockAPI;
+import me.szaki.xkcd.xkcdbrowser.network.api.ComicApi;
+
+@Module
+public class MockNetworkModule {
+    @Singleton
+    @Provides
+    public ComicApi provideComicApi() {
+        return new MockAPI();
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockComicDB.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockComicDB.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b2461d1dd569afdf5f3c2cdd879a36e2091ddcb
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockComicDB.java
@@ -0,0 +1,28 @@
+package me.szaki.xkcd.xkcdbrowser.mock.db;
+
+import android.arch.persistence.db.SupportSQLiteOpenHelper;
+import android.arch.persistence.room.DatabaseConfiguration;
+import android.arch.persistence.room.InvalidationTracker;
+
+import me.szaki.xkcd.xkcdbrowser.database.ComicsDAO;
+import me.szaki.xkcd.xkcdbrowser.database.ComicsDatabase;
+
+
+public class MockComicDB extends ComicsDatabase {
+    private MockDAO mockDAO = new MockDAO();
+
+    @Override
+    public ComicsDAO getDAO() {
+        return mockDAO;
+    }
+
+    @Override
+    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
+        return null;
+    }
+
+    @Override
+    protected InvalidationTracker createInvalidationTracker() {
+        return null;
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockDAO.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockDAO.java
new file mode 100644
index 0000000000000000000000000000000000000000..656f20f1ada99fe20c3069e82ae2d9f6693a6536
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/mock/db/MockDAO.java
@@ -0,0 +1,65 @@
+package me.szaki.xkcd.xkcdbrowser.mock.db;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import me.szaki.xkcd.xkcdbrowser.database.ComicsDAO;
+import me.szaki.xkcd.xkcdbrowser.model.ComicStrip;
+
+
+public class MockDAO implements ComicsDAO {
+    List<ComicStrip> comicStrip = new ArrayList<>();
+
+    public MockDAO() {
+        comicStrip.add(new ComicStrip(){{
+            setImg("https://cataas.com/c");
+            setDay("4");
+            setMonth("4");
+            setNum(10L);
+            setTitle("Title");
+            setYear("2000");
+        }});
+    }
+
+    @Override
+    public List<ComicStrip> getAllComics() {
+        return comicStrip;
+    }
+
+    @Override
+    public ComicStrip getComic(Long id) {
+        return comicStrip.get(Math.toIntExact(id));
+    }
+
+    @Override
+    public ComicStrip getComicByNum(Long num) {
+        for (ComicStrip c: comicStrip) {
+            if (c.getNum().equals(num)){
+                return c;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void insertAll(ComicStrip... comic) {
+        comicStrip.addAll(Arrays.asList(comic));
+    }
+
+    @Override
+    public void update(ComicStrip comic) {
+        delete(comic);
+        comicStrip.add(comic);
+    }
+
+    @Override
+    public void delete(ComicStrip comic) {
+        for (ComicStrip c: comicStrip) {
+            if (c.equals(comic)){
+                comicStrip.remove(c);
+                return;
+            }
+        }
+    }
+}
diff --git a/app/src/test/java/me/szaki/xkcd/xkcdbrowser/test/MainTest.java b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/test/MainTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..759597ea923381ffa95bdf507ede229f9ed470c7
--- /dev/null
+++ b/app/src/test/java/me/szaki/xkcd/xkcdbrowser/test/MainTest.java
@@ -0,0 +1,144 @@
+package me.szaki.xkcd.xkcdbrowser.test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import me.szaki.xkcd.xkcdbrowser.BuildConfig;
+import me.szaki.xkcd.xkcdbrowser.TestHelper;
+import me.szaki.xkcd.xkcdbrowser.network.model.Comic;
+import me.szaki.xkcd.xkcdbrowser.ui.main.MainPresenter;
+import me.szaki.xkcd.xkcdbrowser.ui.main.MainScreen;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static junit.framework.Assert.assertEquals;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class)
+public class MainTest {
+    private MainPresenter mainPresenter;
+    private MainScreen mainScreen;
+    private CountDownLatch latch;
+
+    @Before
+    public void Setup() {
+        TestHelper.setTestInjector();
+        mainScreen = mock(MainScreen.class);
+        mainPresenter = new MainPresenter();
+        mainPresenter.attachScreen(mainScreen);
+        latch = new CountDownLatch(1);
+    }
+
+    @Test
+    public void getCurrentComic() throws InterruptedException {
+        mainPresenter.getCurrent();
+
+        latch.await(200, TimeUnit.MILLISECONDS);
+
+        ArgumentCaptor<Comic> comiCaptor = ArgumentCaptor.forClass(Comic.class);
+        verify(mainScreen).getImage(comiCaptor.capture());
+
+        assertEquals(10, (int) comiCaptor.getValue().getNum());
+        assertEquals("4", comiCaptor.getValue().getDay());
+        assertEquals("4", comiCaptor.getValue().getMonth());
+        assertEquals("2000", comiCaptor.getValue().getYear());
+        assertEquals("Title", comiCaptor.getValue().getTitle());
+        assertEquals("https://cataas.com/c", comiCaptor.getValue().getImg());
+    }
+
+    @Test
+    public void getPrevious() throws InterruptedException {
+        mainPresenter.getCurrent();
+        latch.await(200, TimeUnit.MILLISECONDS);
+        reset(mainScreen);
+
+        mainPresenter.getPrevious();
+
+        latch.await(200, TimeUnit.MILLISECONDS);
+
+        ArgumentCaptor<Comic> comiCaptor = ArgumentCaptor.forClass(Comic.class);
+        verify(mainScreen).getImage(comiCaptor.capture());
+
+        assertEquals(9, (int) comiCaptor.getValue().getNum());
+        assertEquals("1", comiCaptor.getValue().getDay());
+        assertEquals("1", comiCaptor.getValue().getMonth());
+        assertEquals("1999", comiCaptor.getValue().getYear());
+        assertEquals("Tittle", comiCaptor.getValue().getTitle());
+        assertEquals("https://cataas.com/cat", comiCaptor.getValue().getImg());
+    }
+
+    @Test
+    public void getRandom() {
+
+    }
+
+    @Test
+    public void getNext() throws InterruptedException {
+        mainPresenter.getCurrent();
+        latch.await(200, TimeUnit.MILLISECONDS);
+
+        mainPresenter.getPrevious();
+        latch.await(200, TimeUnit.MILLISECONDS);
+        reset(mainScreen);
+
+        mainPresenter.getNext();
+
+        latch.await(200, TimeUnit.MILLISECONDS);
+
+        ArgumentCaptor<Comic> comiCaptor = ArgumentCaptor.forClass(Comic.class);
+        verify(mainScreen).getImage(comiCaptor.capture());
+
+        assertEquals(10, (int) comiCaptor.getValue().getNum());
+        assertEquals("4", comiCaptor.getValue().getDay());
+        assertEquals("4", comiCaptor.getValue().getMonth());
+        assertEquals("2000", comiCaptor.getValue().getYear());
+        assertEquals("Title", comiCaptor.getValue().getTitle());
+        assertEquals("https://cataas.com/c", comiCaptor.getValue().getImg());
+    }
+
+    @Test
+    public void getNextIfLast() throws InterruptedException {
+        mainPresenter.getCurrent();
+        latch.await(200, TimeUnit.MILLISECONDS);
+        reset(mainScreen);
+
+        mainPresenter.getNext();
+
+        latch.await(200, TimeUnit.MILLISECONDS);
+
+        verify(mainScreen).getImage(null);
+    }
+
+
+    @Test
+    public void saveComic() throws InterruptedException {
+        mainPresenter.getCurrent();
+        latch.await(200, TimeUnit.MILLISECONDS);
+        ArgumentCaptor<Comic> comiCaptor = ArgumentCaptor.forClass(Comic.class);
+        verify(mainScreen).getImage(comiCaptor.capture());
+
+        mainPresenter.saveComic(comiCaptor.getValue());
+        latch.await(200, TimeUnit.MILLISECONDS);
+        verify(mainScreen).saveComic();
+
+        reset(mainScreen);
+        mainPresenter.getCurrent();
+        latch.await(200, TimeUnit.MILLISECONDS);
+        verify(mainScreen).setFavorite(true);
+    }
+
+    @After
+    public void Teardown() {
+        mainPresenter.detachScreen();
+    }
+}