Подтвердить что ты не робот

Модульное тестирование создания базы данных SQLite с использованием Spock и Robospock

spock-core:0.7-groovy-2.0
robospock:0.5.0
Android Studio 0.8.2
Fedora release 20 (Heisenbug)

Это полное решение. Теперь он скомпилирует и запускает unit test успешно, а структура каталогов совпадает с редактированием предварительного просмотра. Пожалуйста, не стесняйтесь комментировать все, что не выглядит правильным.

Изменить решение =====

build.gradle:

apply plugin: 'java'
apply plugin: 'groovy'

repositories {
    mavenCentral()

    maven {
        // Location of Android SDK for compiling otherwise get this error:
        /* Could not find com.android.support:support-v4:19.0.1.
           Required by:
           :testSQLite:unspecified > org.robospock:robospock:0.5.0 > org.robolectric:robolectric:2.3 */
        url "/home/steve/local/android-studio/sdk/extras/android/m2repository/"
    }
}

dependencies {
    // just compile so we can use the sqlite API
    compile 'com.google.android:android:4.1.1.4', {
        // Do not bring in dependencies
        transitive = false
    }

    testCompile 'org.codehaus.groovy:groovy:2.3.+'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
    testCompile 'org.robospock:robospock:0.5.0'
    testCompile 'org.robospock:robospock-plugin:0.4.0'
}

SnapzClientTest.groovy:

package com.example.DataAccess

import com.example.DataAccess.SnapzAndroidDB

import org.robolectric.Robolectric
import pl.polidea.robospock.RoboSpecification

class SnapClientTest extends RoboSpecification {

    /* Create Sqlite database for Android */
    def 'Create a sqlite database for Android'() {
        setup:
        def androidDB = new SnapzAndroidDB(Robolectric.application)

        expect:
        androidDB != null
    }
}

SnapzAndroidDB.java, без изменений с 5 августа Редактировать

Edit 5 August ================

В основном, я пытаюсь создать JAR файл, который будет использоваться в приложении для Android, который будет иметь функциональные возможности SQLite, поэтому я могу использовать этот JAR файл для многих приложений.

Я начал с нуля и создал небольшое приложение, которое легче исправить. Это структура каталогов, и есть только три файла:

testSQLite/build.gradle
testSQLite/src/main/java/com/example/sqltest/SnapzAndroidDB.java
testSQLite/src/test/groovy/SnapzClientTest.groovy

build.gradle

apply plugin: 'java'
apply plugin: 'groovy'

repositories {
    mavenCentral()

    maven {
        // Location of Android SDK for compiling otherwise get this error:
        /* Could not find com.android.support:support-v4:19.0.1.
           Required by:
           :testSQLite:unspecified > org.robospock:robospock:0.5.0 > org.robolectric:robolectric:2.3 */
        url "/home/steve/local/android-studio/sdk/extras/android/m2repository/"
    }
}

dependencies {
    // just compile so we can use the sqlite API
    compile 'com.google.android:android:4.1.1.4', {
        // Do not bring in dependencies
        transitive = false
    }

    testCompile 'org.codehaus.groovy:groovy:2.3.+'
    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
    testCompile 'org.robospock:robospock:0.5.0'
    testCompile 'org.robospock:robospock-plugin:0.4.0'
}

SnapzAndroidDB.java

package com.example.DataAccess;

import java.util.logging.ConsoleHandler;
import java.util.logging.SimpleFormatter;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.Level;

import android.content.Context;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteException;
import android.database.Cursor;

public class SnapzAndroidDB extends SQLiteOpenHelper {
    /**
     * Logger for displaying log messages
     */
    private static final Logger log = Logger.getLogger("SnapzAndroidDB");

    private SQLiteDatabase mDb;

    public SnapzAndroidDB(Context context) {
        super(context, "DB_NAME", null, 1);

        /* Create logger */
        ConsoleHandler consoleHandler = new ConsoleHandler();
        log.addHandler(consoleHandler);
        log.setLevel(Level.FINE);
        consoleHandler.setFormatter(new SimpleFormatter());
        consoleHandler.setLevel(Level.ALL);

        log.log(Level.INFO, "SnapzAndroidDB()");
    }

    /* Called only once first time the database is created */
    @Override
    public void onCreate(SQLiteDatabase mDb) {
        log.log(Level.INFO, "onCreate(SQLiteDatabase db)");

        String createConfig = String.format("create table %s (%s int primary key, %s text, %s text)",
                                         "TABLE_CONFIG",
                                         "ID",
                                         "NAME",
                                         "VALUE");

        log.log(Level.INFO, "onCreate with SQL: " + createConfig);
        mDb.execSQL(createConfig);
    }

    @Override
    public void onUpgrade(SQLiteDatabase mDb, int oldVersion, int newVersion) {
        log.log(Level.INFO, "onUpgrade()");
        /* Only if there is some schema changes to the database */
    }
}

SnapzClientTest.groovy

package com.example.DataAccess

import com.example.DataAccess.SnapzAndroidDB

import spock.lang.Specification
import org.robolectric.Robolectric

class SnapClientTest extends Specification {


    /* Create SQLite database for Android */
    def 'Create an SQLite database for Android'() {
        setup:
        def androidDB = new SnapzAndroidDB(Robolectric.application)

        expect:
        androidDB != null
    }
}

Ошибка, которую я все еще получаю, следующая:

com.example.DataAccess.SnapClientTest > Create an SQLite database for Android FAILED
    java.lang.RuntimeException: Stub!
        at android.database.sqlite.SQLiteOpenHelper.<init>(SQLiteOpenHelper.java:4)
        at com.example.DataAccess.SnapzAndroidDB.<init>(SnapzAndroidDB.java:26)
        at com.example.DataAccess.SnapClientTest.Create a sqlite database for Android(SnapzClientTest.groovy:15)

Редактировать 4 августа ===================

Это моя обновленная спецификация теста, которая использует Robolectric для генерации контекста, который может использоваться в конструкторе SQLiteOpenHelper (...)

import org.robolectric.Robolectric

def 'Create an SQLite database for Android'() {
    setup:
    def androidDB = new SnapzAndroidDB(Robolectric.application)

    expect:
    androidDB != null
}

Функция, которую я фактически тестирую, представляет собой класс, который расширяет SQLiteOpenHelper. И мой конструктор SnapzAndroidDB(...) вызывает конструктор SQLiteOpenHelper(), так как вы можете видеть, что контекст - это первый параметр, который передается из тестовой спецификации:

public class SnapzAndroidDB extends SQLiteOpenHelper
    public SnapzAndroidDB(Context context) {
        super(context, SnapzContract.DB_NAME, null, SnapzContract.DB_VERSION);       
    }
    .
    .
}

Когда я запускаю свой тест, я получаю эту ошибку:

com.sunsystem.HttpSnapClient.SnapClientTest > Create an SQLite database for Android FAILED
    java.lang.RuntimeException: Stub!
        at android.database.sqlite.SQLiteOpenHelper.<init>(SQLiteOpenHelper.java:4)
        at com.sunsystem.DataAccess.SnapzAndroidDB.<init>(SnapzAndroidDB.java:33)
        at com.sunsystem.HttpSnapClient.SnapClientTest.Create a sqlite database for Android(SnapClientTest.groovy:168)

END EDIT =======================

Изменить ====

Когда я пытаюсь использовать getBaseContext(), я получаю следующую ошибку:

com.sunsystem.HttpSnapClient.SnapClientTest > Create an SQLite database for Android FAILED
    groovy.lang.MissingMethodException: No signature of method: com.sunsystem.HttpSnapClient.SnapClientTest.getBaseContext() is applicable for argument types: () values: []
        at com.sunsystem.HttpSnapClient.SnapClientTest.Create a sqlite database for Android(SnapClientTest.groovy:159)

Моя функция spock для этой функции:

def 'Create an SQLite database for Android'() {
    setup:
    def androidDB = new SnapzAndroidDB(getBaseContext())

    expect:
    androidDB != null
}

Вот зависимости:

dependencies {
    compile "com.googlecode.json-simple:json-simple:1.1.1", {
        // Exclude junit as we don't want this include in our JAR file as it will add hamcast and other dependencies as well
        exclude group:'junit', module: 'junit'
    }

    // Just compile so we can use the SQLite API. This won't be included in the JAR
    compile 'com.google.android:android:4.1.1.4', {
        // Do not bring in dependencies
        transitive = false
    }

    // Compile for unit testing only
    testCompile "org.codehaus.groovy:groovy:2.3.4"
    testCompile "org.spockframework:spock-core:0.7-groovy-2.0"
    testCompile 'org.robospock:robospock:0.5.0'
    testCompile 'com.google.android:android-test:4.1.1.4'
    testCompile 'com.android.tools.build:gradle:0.12.2'
    testCompile 'org.robospock:robospock-plugin:0.4.0'
}

====

Я занимаюсь тестированием модулей Spock для своей библиотеки, написанной на Java, которая будет использоваться в моем приложении для Android.

Java JAR файл, который будет развернут в приложении Android для работы с базой данных. Это этот JAR файл, который я тестирую.

Я написал спецификацию Spock для тестирования создания базы данных SQLite.

В моем Java JAR файле у меня есть класс, который создает базу данных SQLite, и я хочу проверить это в моем Spock unit test.

Однако проблема заключается в том, что конструктор SQLiteOpenHelper должен вызываться с помощью Context, и я пытаюсь высмеять этот контекст, используя import android.text.mock.MockContext в моем Spock unit test.

public class SnapzAndroidDB extends SQLiteOpenHelper implements SnapzDAO {
    public SnapzAndroidDB(Context context) {
        super(context, SnapzContract.DB_NAME, null, SnapzContract.DB_VERSION);    
    }

    /* Called only once first time the database is created */
    @Override
    public void onCreate(SQLiteDatabase db) {
        String sqlCreate = String.format("create table %s (%s int primary key, %s text, %s text, %s text)",
                                         SnapzContract.TABLE,
                                         SnapzContract.GetConfigColumn.ID,
                                         SnapzContract.GetConfigColumn.NAME,
                                         SnapzContract.GetConfigColumn.VALUE,
                                         SnapzContract.GetConfigColumn.CFG_TYPE);
        db.execSQL(sqlCreate);
    }
    .
    .
}

Теперь в моей спецификации тестирования устройства у меня есть это в моем SnapClientTest.groovy:

import android.test.mock.MockContext

def 'Create an SQLite database for Android'() {
    setup:
    def context = new MockContext()
    def androidDB = new SnapzAndroidDB(context.getApplicationContext())

    expect:
    androidDB != null
}

Из этого вы можете видеть, что я издеваюсь над контекстом и отправляю его как параметр в конструктор моего класса, который вызовет конструктор SQLiteOpenHelper.

Ошибка, которую я получаю при запуске my unit test, такова:

com.HttpSnapClient.SnapClientTest > Create an SQLite database for Android FAILED
11:05:27.062 [DEBUG] [TestEventLogger]     java.lang.RuntimeException: Stub!
11:05:27.063 [DEBUG] [TestEventLogger]         at android.content.Context.<init>(Context.java:4)
11:05:27.063 [DEBUG] [TestEventLogger]         at android.test.mock.MockContext.<init>(MockContext.java:5)
11:05:27.063 [DEBUG] [TestEventLogger]         at com.sunsystem.HttpSnapClient.SnapClientTest.Create a sqlite database for Android(SnapClientTest.groovy:155)
11:05:27.065 [QUIET] [system.out] 11:05:27.064 [DEBUG] [org.gradle.process.internal.child.ActionExecutionWorker] Stopping client connection.

Будучи новым для Spock Я не уверен, что это возможно или нет, поскольку я просто тестирую свой JAR файл.

4b9b3361

Ответ 1

Проблема, с которой вы сталкиваетесь, - это правильный контекст для создания БД.

Ваша первая попытка с getBaseContext() не работала, так как не обнаружила такую ​​функцию в SnapClientTest: "Нет сигнатуры метода"

Во второй попытке вы создаете экземпляр MockContext - это незавершенная реализация, которую вы не можете использовать напрямую.

http://developer.android.com/reference/android/test/mock/MockContext.html "Макет класса" Контекст ". Все методы нефункциональны и выбрасывают UnsupportedOperationException."

Try:

def androidDB = new SnapzAndroidDB(Robolectric.application)

Согласование http://robospock.org/ Robolectric.application должно дать рабочий контекст.

Обновление Я просто заметил, что вы не распространяете RoboSpecification, но Спецификация:

import pl.polidea.robospock.RoboSpecification

и

class SnapClientTest extends RoboSpecification

Ответ 2

Spock - одна из наиболее широко используемых фреймворков в экосистеме Groovy и Java, которая позволяет создавать тесты BDD в очень интуитивно понятный язык и облегчает некоторые общие задачи, такие как насмешка и расширяемость. Что отличает его от толпы, так это ее прекрасный и очень выразительный язык спецификаций. Благодаря своему игроку JUnit Spock совместим с большинством IDE, инструментами построения и серверами непрерывной интеграции. Чтобы работать со Spock, вам в основном нужно выполнить набор шагов, например следующий рецепт, который позволит вам эффективно реализовать как unit test и веб-интеграции.

Ваше текущее сообщение об ошибке:

Create a sqlite database for Android FAILED

Попробуйте выполнить следующие действия и посмотрите, как это сделать:

Включение в свой код getWritableDatabase и getReadableDatabase должно помочь:

jokesHelper dbHelper = new jokesHelper(getBaseContext());
SQLiteDatabase db = dbHelper.getWritableDatabase();

Таким образом, Android сможет управлять и кэшировать connection.

Тем не менее, если у вас есть сообщение об ошибке из getBaseContext, попробуйте удалить плагин тестирования и заново создать ресурсы STS (.classpath и .project) с помощью метода integrate-with -eclipse, тогда он должен работает.

Если у вас возникли проблемы с getSpecificationContext, это означает, что некоторые детали были опущены, и вам нужно дважды проверить свои спецификации.

Если вы не используете Eclipse, создайте Java Jar с помощью spock, вы можете связать его с Emacs с помощью командной строки Java development как обычный, например Suns JDK или любой другой подход, ожидаемый для Enterprise Development. Чтобы запускать SampleTest, вы должны вызвать тестовую задачу из командной строки с помощью свойства Java system:

gradle -Dtest.single=Sample test

Или, альтернативно,

gradle -Dtest.single=SoapTest clean test

Также проверьте, какие права используются в используемом каталоге. И если вы еще не сделали этого, не забудьте включить зависимости:

dependencies {
    classpath 'com.android.tools.build:gradle:0.8.+'
    classpath 'org.robospock:robospock-plugin:0.4.0'
}

И сообщите тестовому каталогу, что вы используя, например. srcDirs. И имейте в виду, что важно импортировать правильный ресурс по классу. Как таковой, также include для "build.gradle" в "defaultConfig":

testPackageName "com.yourpackage.test"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
testFunctionalTest true

Spock и Robospock являются инновационными инструментами, которые могут помочь полезным ресурсам для разработки unit test. В качестве альтернативы вы также можете использовать такие инструменты, как TCL Tests. Тесты TCL - это самый старый набор тестов для SQLite и это лучший подход, который вы можете предпринять. Фактически, SQLite начал жизнь как расширение Tcl. Большая часть инструментов тестирования и разработки для SQLite написана в Tcl. В дополнение к собственному API C, расширение Tcl является единственным API, поддерживаемым основной командой SQLite.

Чтобы включить привязки Tcl, загрузите распределение источника SQLite TEA (Tcl Extension Architecture) из сайта SQLite. Эта версия кода - это, по сути, распределение амальгамирования с Tcl привязывается к концу. Это будет встроено в расширение Tcl, которое затем может быть импортировано в любую среду Tcl.

enter image description here

Необходимо соблюдать конкретные шаги, и важно внимание к каждому detail, так как это может иметь значение для успешного тестирования. запустить или нет.

инструментальная среда является основой структуры тестирования. Инструментарий контролирует тестируемое приложение и позволяет впрыскивать макетные компоненты, необходимые для запуска приложения. Например, вы можете создавать mock Contexts перед запуском приложения и использовать приложение.

enter image description here

Все взаимодействие приложения с окружающей средой можно контролировать с помощью этого подхода. Вы также можете изолировать свое приложение в ограниченной среде, чтобы иметь возможность прогнозировать результаты, форсировать значения, возвращаемые некоторыми методами, или издеваться над постоянными и неизменными данными для ContentProvider, баз данных или даже содержимого файловой системы. Поэтому также важно указать в своей деятельности информацию, которую вы используете test:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.aatg.sample.test"
        android:versionCode="1" android:versionName="1.0">
        <application android:icon="@drawable/icon"
            android:label="@string/app_name">
            <uses-library android:name="android.test.runner" />
        </application>
        <uses-sdk android:minSdkVersion="7" />
        <instrumentation
            android:targetPackage="com.example.aatg.sample
            android:name="android.test.InstrumentationTestRunner"
            android:label="Sample Tests" />
        <uses-permission android:name="
            android.permission.INJECT_EVENTS" />
    </manifest>

Если вы запускаете JNI для управления своей базой данных с помощью собственного кода, есть два способа загрузить расширение с помощью SQLite. Один из них - через вызов API C, а один - через функцию SQL, которая ссылается на тот же код, что и функция C API. В обоих случаях вы указываете имя файла и, необязательно, имя функции точки входа:

int sqlite3_load_extension( sqlite3 *db, const char *ext_name,
    const char *entry_point, char **error )

Другим способом загрузки загружаемого расширения является встроенная функция SQL:

load_extension( 'ext_name' )
load_extension( 'ext_name', 'entry_point' )

Эта функция аналогична вызову C sqlite3_load_extension() с одним основным ограничением. Поскольку это функция SQL, когда она вызывается, по определению будет выполняться оператор SQL, когда загружается расширение. Это означает, что любое расширение, загруженное с помощью функции load_extension() SQL, полностью не сможет переопределить или удалить пользовательскую функцию, включая специализированный набор функций like(). Подходы к загрузке данных с помощью подходящего синтаксиса работают аналогично Java, поскольку ожидается.

enter image description here

Директивы отладки используются только для тестирования и разработки поскольку они добавляют значительные накладные расходы и заставляют все работать заметно медленнее, точно так же, как включение throws Exception. Когда вы выполняете unit test, вам необходимо установить , а также проверить, чтобы ваша база данных не получала corrupted. В принципе, улучшение наилучшей настройки ваших параметров отладки улучшится и поможет вам плавно перейти на наилучшим образом выполнить ваше тестирование. p >

В дополнение ко всем другим директивам сборки SQLite имеет достаточное количество директив SQLite_OMIT_ * для компиляции. Они предназначены для удаления основных функций из сборки, чтобы сделать базовую библиотеку баз данных как можно более компактной и компактной. Чтобы использовать большинство этих директив, вы должны создавать SQLite из источников разработки, найденных в дереве управления версиями. Большинство пропущенных директив не работают правильно, когда применяется к источнику распространения или к предварительно построенному объединению. Также имейте в виду, что эти директивы времени компиляции официально не поддерживаются в том смысле, что они не являются частью официальной цепочки тестирования. Для любой данной версии SQLite могут возникать проблемы с компиляцией и проблемами времени выполнения, если разрешены произвольные флаги опускания.

enter image description here

Конечно, вам не нужно быть самурай для запуска unit test для SQLite на Android, хотя это может помочь.