Android-ProviderTestCase2 API 级别 28 不工作
Android-ProviderTestCase2 with API Level 28 not working
前段时间我使用 ProviderTestCase2 创建了一些插桩测试。现在我想更新我的代码库并尝试在当前环境中重新激活我的旧测试。
1) 当我更新到 compileSdkVersion 28 时,似乎不再有 class ProviderTestCase2。清理和构建项目失败。
如果我回到版本 27,我可以成功构建项目。
2) 我有一个特殊情况,我想用一组测试来测试一个对象的不同变体。所以我决定利用继承,将测试放在基class中,配置要由superclass测试的对象。这在过去曾奏效。 (是的,我知道测试中的继承不好的观点,但在这种情况下没关系;-))
现在没有执行任何测试。 Android Studio 抱怨有一个空的测试套件。
还有警告说我在 class 扩展 JUnit3 中使用 JUnit4 注释进行了测试。我不记得我以前看过这些警告。
这是 Android 库中的错误吗?或者我在哪里可以找到一些解决此问题的提示?
(使用 Android Studio 3.2 和最新的库)
ProviderTestCase2 was removed。应改用 ProviderTestRule
(来自支持测试库或 AndroidX)。
参见:
- Android Developers Blog: Android Testing Support Library 1.0 is here! (see heading ProviderTestRule) 和
- Android Developer Reference: ProviderTestRule
添加 com.android.support.test:rules
依赖项,以便能够使用规则。
这是参考页面中的示例:
@Rule
public ProviderTestRule mProviderRule =
new ProviderTestRule.Builder(MyContentProvider.class, MyContentProvider.AUTHORITY).build();
@Test
public void verifyContentProviderContractWorks() {
ContentResolver resolver = mProviderRule.getResolver();
// perform some database (or other) operations
Uri uri = resolver.insert(testUrl, testContentValues);
// perform some assertions on the resulting URI
assertNotNull(uri);
}
接受的答案不正确。 ProviderTestRule
目前处于测试阶段,ProviderTestCase2
没有弃用或删除。发生的事情是它已被移至您需要在 build.gradle
:
中声明的库
useLibrary 'android.test.base'
参见:https://developer.android.com/training/testing/set-up-project
TLDR
build.gradle
:
android {
useLibrary 'android.test.base' // for AndroidTestCase
useLibrary 'android.test.runner' // for ProviderTestCase2
useLibrary 'android.test.mock' // for MockContentProvider
}
Android 有关各种基于 JUnit
的库的文档,可用于 useLibrary
调用:https://developer.android.com/training/testing/set-up-project#junit-based-libs
进一步说明
方向正确,但还不完整。
首先,要导入 ProviderTestCase2
,您需要
build.gradle
:
android {
useLibrary 'android.test.runner'
}
但是,ProviderTestCase2
扩展了 AndroidTestCase
,因此您需要:
build.gradle
:
android {
useLibrary 'android.test.base'
}
最后,如果要扩展 ProviderTestCase2
,则需要提供扩展 ContentProvider
的类型,并且可能需要扩展 MockContentProvider
才能创建它。对于 MockContentProvider
,您需要 ():
build.gradle
:
android {
useLibrary 'android.test.mock'
}
尝试 ProviderTestRule 多次测试我的自定义 ContentProvider class 但仅当将我的测试 class 添加到 [=24= 时才成功]androidTest 文件夹,这里是示例 kotlin 代码:
//this is my custom provider class
public final class NameProvider : ContentProvider() {
private var pContext: Context? = null;
private var nameDbHelper: NameDbHelper? = null
private var dataBase: SQLiteDatabase? = null
private val Log_Tag = "NAME_PROVIDER"
override fun onCreate(): Boolean {
pContext = context
nameDbHelper = pContext?.let { NameDbHelper(it) }
dataBase = nameDbHelper?.writableDatabase
if (dataBase == null) {
Log.d(Log_Tag, "dataBase NOT created ...")
return false
}
Log.d(Log_Tag, "dataBase created ...")
return true
}
override fun query(p0: Uri, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?): Cursor? {
var queryBuilder : SQLiteQueryBuilder = SQLiteQueryBuilder()
queryBuilder.tables = "TABLE_NAME"
val cursor = queryBuilder.query(dataBase,p1,p2,p3,null,null, "COLUMN_NAME")
cursor.setNotificationUri(pContext?.contentResolver, p0)
return cursor
}
override fun insert(p0: Uri, p1: ContentValues?): Uri? {
val rowID: Long? = dataBase?.insert("TABLE_NAME", "", p1)
rowID?.let {
if (it > 0) {
val _uri = ContentUris.withAppendedId(CONTENT_URI, it)
pContext?.contentResolver?.notifyChange(_uri, null)
return _uri
}
}
throw SQLException("Failed to add a record into")
}
override fun getType(p0: Uri): String? {
TODO("Not yet implemented")
}
override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
TODO("Not yet implemented")
}
}
现在测试 class 我可以 运行 成功 :
@RunWith(AndroidJUnit4::class)
class NameProviderTest {
@get:Rule
var mProviderRule = ProviderTestRule.Builder(NameProvider::class.java, NameContract.AUTHORITY)
.setDatabaseCommands(NameContract.DATABASE_NAME).build()
@Test
fun contentResolverNull(){
val resolver = mProviderRule.resolver
assertNotNull(resolver)
}
@Test
fun query() {
val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mProviderRule.resolver.query(NameContract.CONTENT_URI, null, null, null)
} else {
null
}
assertNotNull(cursor)
var name : String? = null
if(cursor != null && cursor.moveToFirst()){
name = cursor.getString(cursor.getColumnIndex(NameContract.KEY_NAME))
}
assertNotNull(name)
assertEquals(name, "Taher")
}
@Test
fun insert() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "Taher");
val uri = mProviderRule.resolver.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
}
}
更好的方法
但后来我找到了使用 robolectric:4.2 的更好方法。这次感觉像是实际的单元测试,因为我可以通过将我的测试 class NameProviderTest 添加到 test 文件夹来完成此操作。
现在测试 class 看起来像这样:
@Config(maxSdk = 29)
@RunWith(RobolectricTestRunner::class)
class NameProviderTest {
private var contentResolver : ContentResolver? = null
private var shadowContentResolver : ShadowContentResolver? = null
private var nameProvider : NameProvider? = null
@Before
fun setUp() {
contentResolver = ApplicationProvider.getApplicationContext<Context>().contentResolver
val providerInfo : ProviderInfo = ProviderInfo()
providerInfo.authority = NameContract.AUTHORITY
providerInfo.grantUriPermissions = true
val controller = Robolectric.buildContentProvider(NameProvider::class.java).create(providerInfo)
shadowContentResolver = shadowOf(contentResolver)
nameProvider = controller.get()
}
@Test
fun onCreate() {
val res = nameProvider?.onCreate()
assertEquals(res, true)
//ShadowContentResolver.registerProviderInternal(NameContract.AUTHORITY, nameProvider)
}
@Test
fun query() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "ABCD")
val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
val cursor = contentResolver?.query(NameContract.CONTENT_URI,null,null,null,null)
assertNotNull(cursor)
var name : String? = null
if(cursor != null && cursor.moveToFirst()){
name = cursor.getString(cursor.getColumnIndex(NameContract.KEY_NAME))
}
cursor?.close()
assertNotNull(name)
assertEquals(name, "ABCD")
}
@Test
fun insert() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "Taher")
val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
}
}
前段时间我使用 ProviderTestCase2 创建了一些插桩测试。现在我想更新我的代码库并尝试在当前环境中重新激活我的旧测试。
1) 当我更新到 compileSdkVersion 28 时,似乎不再有 class ProviderTestCase2。清理和构建项目失败。 如果我回到版本 27,我可以成功构建项目。
2) 我有一个特殊情况,我想用一组测试来测试一个对象的不同变体。所以我决定利用继承,将测试放在基class中,配置要由superclass测试的对象。这在过去曾奏效。 (是的,我知道测试中的继承不好的观点,但在这种情况下没关系;-))
现在没有执行任何测试。 Android Studio 抱怨有一个空的测试套件。
还有警告说我在 class 扩展 JUnit3 中使用 JUnit4 注释进行了测试。我不记得我以前看过这些警告。
这是 Android 库中的错误吗?或者我在哪里可以找到一些解决此问题的提示?
(使用 Android Studio 3.2 和最新的库)
ProviderTestCase2 was removed。应改用 ProviderTestRule
(来自支持测试库或 AndroidX)。
参见:
- Android Developers Blog: Android Testing Support Library 1.0 is here! (see heading ProviderTestRule) 和
- Android Developer Reference: ProviderTestRule
添加 com.android.support.test:rules
依赖项,以便能够使用规则。
这是参考页面中的示例:
@Rule
public ProviderTestRule mProviderRule =
new ProviderTestRule.Builder(MyContentProvider.class, MyContentProvider.AUTHORITY).build();
@Test
public void verifyContentProviderContractWorks() {
ContentResolver resolver = mProviderRule.getResolver();
// perform some database (or other) operations
Uri uri = resolver.insert(testUrl, testContentValues);
// perform some assertions on the resulting URI
assertNotNull(uri);
}
接受的答案不正确。 ProviderTestRule
目前处于测试阶段,ProviderTestCase2
没有弃用或删除。发生的事情是它已被移至您需要在 build.gradle
:
useLibrary 'android.test.base'
参见:https://developer.android.com/training/testing/set-up-project
TLDR
build.gradle
:
android {
useLibrary 'android.test.base' // for AndroidTestCase
useLibrary 'android.test.runner' // for ProviderTestCase2
useLibrary 'android.test.mock' // for MockContentProvider
}
Android 有关各种基于 JUnit
的库的文档,可用于 useLibrary
调用:https://developer.android.com/training/testing/set-up-project#junit-based-libs
进一步说明
首先,要导入 ProviderTestCase2
,您需要
build.gradle
:
android {
useLibrary 'android.test.runner'
}
但是,ProviderTestCase2
扩展了 AndroidTestCase
,因此您需要:
build.gradle
:
android {
useLibrary 'android.test.base'
}
最后,如果要扩展 ProviderTestCase2
,则需要提供扩展 ContentProvider
的类型,并且可能需要扩展 MockContentProvider
才能创建它。对于 MockContentProvider
,您需要 (
build.gradle
:
android {
useLibrary 'android.test.mock'
}
尝试 ProviderTestRule 多次测试我的自定义 ContentProvider class 但仅当将我的测试 class 添加到 [=24= 时才成功]androidTest 文件夹,这里是示例 kotlin 代码:
//this is my custom provider class
public final class NameProvider : ContentProvider() {
private var pContext: Context? = null;
private var nameDbHelper: NameDbHelper? = null
private var dataBase: SQLiteDatabase? = null
private val Log_Tag = "NAME_PROVIDER"
override fun onCreate(): Boolean {
pContext = context
nameDbHelper = pContext?.let { NameDbHelper(it) }
dataBase = nameDbHelper?.writableDatabase
if (dataBase == null) {
Log.d(Log_Tag, "dataBase NOT created ...")
return false
}
Log.d(Log_Tag, "dataBase created ...")
return true
}
override fun query(p0: Uri, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?): Cursor? {
var queryBuilder : SQLiteQueryBuilder = SQLiteQueryBuilder()
queryBuilder.tables = "TABLE_NAME"
val cursor = queryBuilder.query(dataBase,p1,p2,p3,null,null, "COLUMN_NAME")
cursor.setNotificationUri(pContext?.contentResolver, p0)
return cursor
}
override fun insert(p0: Uri, p1: ContentValues?): Uri? {
val rowID: Long? = dataBase?.insert("TABLE_NAME", "", p1)
rowID?.let {
if (it > 0) {
val _uri = ContentUris.withAppendedId(CONTENT_URI, it)
pContext?.contentResolver?.notifyChange(_uri, null)
return _uri
}
}
throw SQLException("Failed to add a record into")
}
override fun getType(p0: Uri): String? {
TODO("Not yet implemented")
}
override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
TODO("Not yet implemented")
}
}
现在测试 class 我可以 运行 成功 :
@RunWith(AndroidJUnit4::class)
class NameProviderTest {
@get:Rule
var mProviderRule = ProviderTestRule.Builder(NameProvider::class.java, NameContract.AUTHORITY)
.setDatabaseCommands(NameContract.DATABASE_NAME).build()
@Test
fun contentResolverNull(){
val resolver = mProviderRule.resolver
assertNotNull(resolver)
}
@Test
fun query() {
val cursor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mProviderRule.resolver.query(NameContract.CONTENT_URI, null, null, null)
} else {
null
}
assertNotNull(cursor)
var name : String? = null
if(cursor != null && cursor.moveToFirst()){
name = cursor.getString(cursor.getColumnIndex(NameContract.KEY_NAME))
}
assertNotNull(name)
assertEquals(name, "Taher")
}
@Test
fun insert() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "Taher");
val uri = mProviderRule.resolver.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
}
}
更好的方法
但后来我找到了使用 robolectric:4.2 的更好方法。这次感觉像是实际的单元测试,因为我可以通过将我的测试 class NameProviderTest 添加到 test 文件夹来完成此操作。
现在测试 class 看起来像这样:
@Config(maxSdk = 29)
@RunWith(RobolectricTestRunner::class)
class NameProviderTest {
private var contentResolver : ContentResolver? = null
private var shadowContentResolver : ShadowContentResolver? = null
private var nameProvider : NameProvider? = null
@Before
fun setUp() {
contentResolver = ApplicationProvider.getApplicationContext<Context>().contentResolver
val providerInfo : ProviderInfo = ProviderInfo()
providerInfo.authority = NameContract.AUTHORITY
providerInfo.grantUriPermissions = true
val controller = Robolectric.buildContentProvider(NameProvider::class.java).create(providerInfo)
shadowContentResolver = shadowOf(contentResolver)
nameProvider = controller.get()
}
@Test
fun onCreate() {
val res = nameProvider?.onCreate()
assertEquals(res, true)
//ShadowContentResolver.registerProviderInternal(NameContract.AUTHORITY, nameProvider)
}
@Test
fun query() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "ABCD")
val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
val cursor = contentResolver?.query(NameContract.CONTENT_URI,null,null,null,null)
assertNotNull(cursor)
var name : String? = null
if(cursor != null && cursor.moveToFirst()){
name = cursor.getString(cursor.getColumnIndex(NameContract.KEY_NAME))
}
cursor?.close()
assertNotNull(name)
assertEquals(name, "ABCD")
}
@Test
fun insert() {
val values = ContentValues()
values.put(NameContract.KEY_NAME, "Taher")
val uri = contentResolver?.insert(NameContract.CONTENT_URI, values);
assertNotNull(uri)
}
}