使用 Spock 和 Robospock 创建 SQLite 数据库的单元测试
spock-core:0.7-groovy-2.0
robospock:0.5.0
Android Studio 0.8.2
Fedora release 20 (Heisenbug)
这是完整的解决方案。现在它编译并成功运行单元测试,并且目录结构与预览编辑相同。请随时对任何看起来不对劲的东西发表评论。
编辑解决方案 =====
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,与8月5日编辑相比没有变化
Edit 5 August ================
基本上,我正在尝试创建一个JAR文件,该文件将用于具有SQLite功能的Android应用程序中,因此我可以将此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)
结束编辑 ==========================
编辑 ====
当我尝试使用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'
}
====
我正在为我的Java编写的库进行Spock单元测试,这些库将在我的Android应用程序中使用。
将部署到 Android 应用程序中以执行数据库操作的 Java JAR 文件。我正在测试的就是这个JAR文件。
我已经编写了一个Spock规范来测试SQLite数据库的创建。
在我的Java JAR文件中,我有一个创建SQLite数据库的类,我想在我的Spock单元测试中对其进行测试。
但是,问题在于构造函数需要使用上下文调用,并且我试图在我的Spock单元测试中使用该上下文来模拟该上下文。SQLiteOpenHelper
import android.text.mock.MockContext
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构造函数。
当我运行单元测试时,我得到的错误是这样的:
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文件。