使用Spock和Robospock创建SQLite数据库的单元测试

ant*_*009 18 java sqlite android unit-testing robospock

spock-core:0.7-groovy-2.0
robospock:0.5.0
Android Studio 0.8.2
Fedora release 20 (Heisenbug)
Run Code Online (Sandbox Code Playgroud)

这是完整的解决方案.现在它成功编译并运行单元测试,目录结构与预览编辑相同.请随意评论任何看起来不正确的事情.

编辑解决方案=====

的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'
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

SnapzAndroidDB.java,8月5日编辑没有变化

Edit 5 August ================
Run Code Online (Sandbox Code Playgroud)

基本上,我正在尝试创建一个JAR文件,该文件将在具有SQLite功能的Android应用程序中使用,因此我可以将此JAR文件用于许多应用程序.

我从头开始创建了一个更容易修复错误的小应用程序.这是目录结构,只有三个文件:

testSQLite/build.gradle
testSQLite/src/main/java/com/example/sqltest/SnapzAndroidDB.java
testSQLite/src/test/groovy/SnapzClientTest.groovy
Run Code Online (Sandbox Code Playgroud)

的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'
}
Run Code Online (Sandbox Code Playgroud)

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 */
    }
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我仍然得到的错误如下:

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)
Run Code Online (Sandbox Code Playgroud)

编辑8月4日===================

这是我更新的测试规范,它使用Robolectric生成可以在SQLiteOpenHelper(...)的构造函数中使用的上下文

import org.robolectric.Robolectric

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

    expect:
    androidDB != null
}
Run Code Online (Sandbox Code Playgroud)

我实际测试的函数是一个扩展的类SQLiteOpenHelper.我的构造函数SnapzAndroidDB(...)调用SQLiteOpenHelper()构造函数,因为您可以看到上下文是从测试规范传递的第一个参数:

public class SnapzAndroidDB extends SQLiteOpenHelper
    public SnapzAndroidDB(Context context) {
        super(context, SnapzContract.DB_NAME, null, SnapzContract.DB_VERSION);       
    }
    .
    .
}
Run Code Online (Sandbox Code Playgroud)

当我运行我的测试时,我收到此错误:

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)
Run Code Online (Sandbox Code Playgroud)

结束编辑=======================

编辑====

当我尝试使用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)
Run Code Online (Sandbox Code Playgroud)

我的规范spock函数是这样的:

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

    expect:
    androidDB != null
}
Run Code Online (Sandbox Code Playgroud)

以下是依赖项:

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'
}
Run Code Online (Sandbox Code Playgroud)

====

我正在为我用我的Android应用程序中使用的Java编写的库进行Spock单元测试.

将部署到Android应用程序以执行数据库操作的Java JAR文件.这是我正在测试的这个JAR文件.

我编写了一个Spock规范来测试SQLite数据库的创建.

在我的Java JAR文件中,我有一个创建SQLite数据库的类,我想在我的Spock单元测试中测试它.

但是,问题是SQLiteOpenHelper需要使用Context调用构造函数,并且我正在尝试使用import android.text.mock.MockContextSpock单元测试来模拟该上下文.

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);
    }
    .
    .
}
Run Code Online (Sandbox Code Playgroud)

现在在我的单元测试规范中,我在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
}
Run Code Online (Sandbox Code Playgroud)

从这里你可以看到我正在模拟上下文并将其作为参数发送到我的类的构造函数,它将调用SQLiteOpenHelper构造函数.

我运行单元测试时得到的错误是这样的:

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.
Run Code Online (Sandbox Code Playgroud)

作为Spock的新手,我不确定这是否可行,因为我只是在测试我的JAR文件.

Ava*_*anz 16

Spock是Groovy和Java生态系统中使用最广泛的框架之一,它允许以非常直观的语言创建BDD测试,并促进一些常见任务,如模拟和可扩展性.它从人群中脱颖而出的是它美丽而富有表现力的规范语言.由于其JUnit运行程序,Spock与大多数IDE,构建工具和持续集成服务器兼容.要使用Spock,您基本上需要执行一系列步骤,例如遵循配方,这将允许您有效地实现单元测试和Web集成.

您当前的错误消息显示为:

Create a sqlite database for Android FAILED
Run Code Online (Sandbox Code Playgroud)

尝试这些步骤,看看它是如何进行的:

包括你的代码getWritableDatabasegetReadableDatabase应该有帮助:

jokesHelper dbHelper = new jokesHelper(getBaseContext());
SQLiteDatabase db = dbHelper.getWritableDatabase();
Run Code Online (Sandbox Code Playgroud)

这样,Android将能够管理和缓存连接.

然而,如果您从getBaseContext收到任何错误消息,请尝试卸载测试插件并使用integrate-with --eclipse重新创建STS资源(.classpath和.project),然后它应该可以工作.

如果您对getSpecificationContext有任何问题,则表示遗漏了一些细节,您需要仔细检查您的规格.

如果您不使用Eclipse,要使用spock 创建Java Jar文件,您可以像往常一样通过命令行Java开发工具将其与Emacs连接,例如Sun的JDK或企业开发预期的任何其他方法.要仅运行SampleTest,必须使用Java系统属性从命令行调用测试任务:

gradle -Dtest.single=Sample test
Run Code Online (Sandbox Code Playgroud)

或者

gradle -Dtest.single=SoapTest clean test
Run Code Online (Sandbox Code Playgroud)

还要检查正在使用的目录的权限.如果还没有完成,请记住包含依赖项:

dependencies {
    classpath 'com.android.tools.build:gradle:0.8.+'
    classpath 'org.robospock:robospock-plugin:0.4.0'
}
Run Code Online (Sandbox Code Playgroud)

并通知您正在使用的测试目录,例如srcDirs.请记住("Es ist wichtig das man hier die richtige Resources-Klasse importiert")导入类所需的正确资源非常重要.因此,还包括 "defaultConfig"中的"build.gradle":

testPackageName "com.yourpackage.test"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
testFunctionalTest true
Run Code Online (Sandbox Code Playgroud)

斯波克Robospock是可以帮助有用的资源,开发创新型工具的单元测试.或者,您也可以使用TCL测试等工具.TCL测试是SQLite最古老的测试集,是您可以采用的最佳方法.实际上,SQLite起源于Tcl扩展.SQLite的许多测试和开发工具都是用Tcl编写的.除了本机C API之外,Tcl扩展是SQLite核心团队支持的唯一API.

要启用Tcl绑定,请从SQLite网站下载SQLite源的TEA(Tcl扩展体系结构)分发.此版本的代码基本上是合并分发,其中Tcl绑定附加到末尾.这将构建为Tcl扩展,然后可以将其导入任何Tcl环境.

在此输入图像描述

应该遵循非常具体的步骤,注意每一个细节都是必不可少的,因为它可以使您的测试成功运行与否.

仪器架构为测试框架的基础.Instrumentation控制正在测试的应用程序,并允许注入应用程序运行所需的模拟组件.例如,您可以在应用程序启动之前创建模拟上下文,并让应用程序使用它们.

在此输入图像描述

可以使用此方法控制应用程序与周围环境的所有交互.您还可以在受限制的环境中隔离应用程序,以便能够预测结果,强制某些方法返回的值或模拟ContentProvider,数据库甚至文件系统内容的持久和未更改的数据.因此,在您的活动中指定您正在运行测试的信息也很重要:

<?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>
Run Code Online (Sandbox Code Playgroud)

如果您运行JNI以使用本机代码操作数据库,则有两种方法可以使用SQLite加载扩展.一种是通过C API调用,一种是通过SQL函数调用与C API函数相同的代码.在这两种情况下,您都提供文件名和(可选)入口点函数的名称:

int sqlite3_load_extension( sqlite3 *db, const char *ext_name,
    const char *entry_point, char **error )
Run Code Online (Sandbox Code Playgroud)

加载可加载扩展的另一种方法是使用内置的SQL函数:

load_extension( 'ext_name' )
load_extension( 'ext_name', 'entry_point' )
Run Code Online (Sandbox Code Playgroud)

此函数类似于C sqlite3_load_extension()调用,但有一个主要限制.因为这是一个SQL函数,所以在调用它时,根据定义,将在加载扩展时执行一条SQL语句.这意味着任何使用load_extension()SQL函数加载的扩展都将完全无法重新定义或删除自定义函数,包括一组专门的like()函数.正如预期的那样,使用合适的语法加载数据的方法与Java类似.

在此输入图像描述

调试指令仅用于测试和开发目的,因为它们会增加显着的开销并使一切运行明显变慢,就像包含抛出异常一样.当你运行单元测试,你需要将它们设置相应的,同时检查,以避免您的数据库没有得到破坏.基本上,为调试设置实现最佳调整将改进并帮助您顺利地以最佳方式运行测试.

除了所有其他构建指令外,SQLite还有相当数量的SQLITE_OMIT_*编译时指令.这些旨在从构建中删除核心功能,以使核心数据库库尽可能小巧紧凑.为了使用大多数这些省略指令,您需要从源代码控制树中的开发源构建SQLite.当应用于源分发或预构建的合并时,大多数省略指令将无法正常工作.还要注意,这些编译时指令不是官方支持的,因为它们不是官方测试链的一部分.对于任何给定版本的SQLite,如果启用了任意的省略标志集,则可能存在编译问题和运行时问题.

在此输入图像描述

当然,你不需要成为一名武士来为Android上的SQLite运行单元测试,尽管它可能有所帮助.


Raa*_*nan 4

您面临的问题是获取用于创建数据库的正确上下文。

您对 getBaseContext() 的第一次尝试没有成功,因为它没有找到像 SnapClientTest 中那样的函数:“没有方法签名”

在第二次尝试中,您将创建一个 MockContext 实例 - 这是一个不能直接使用的存根实现。

http://developer.android.com/reference/android/test/mock/MockContext.html “模拟 Context 类。所有方法都是非功能性的,并抛出 UnsupportedOperationException。”

尝试:

def androidDB = new SnapzAndroidDB(Robolectric.application)
Run Code Online (Sandbox Code Playgroud)

根据http://robospock.org/Robolectric.application 应该给出一个工作上下文。

更新 我刚刚注意到您没有扩展 RoboSpecification 而是扩展规范:

import pl.polidea.robospock.RoboSpecification
Run Code Online (Sandbox Code Playgroud)

class SnapClientTest extends RoboSpecification
Run Code Online (Sandbox Code Playgroud)