標籤:android 單元測試 android-studio
udacity android 實踐筆記: lesson 4 part b
乾貨店打雜的 /titer1 /Archimedes
出處:https://code.csdn.net/titer1
聯絡:1307316一九六八(簡訊最佳)
聲明:本文採用以下協議進行授權: 自由轉載-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,轉載請註明作者及出處。
tips:https://code.csdn.net/titer1/pat_aha/blob/master/Markdown/android/
摘要
- 完全展示content provider添加的過程
- 動態展示android單元測試的過程
動圖出沒喔!o(^▽^)o,動圖在最後
overviw 圖
還是老圖,(create / insert /query)
下面 我們開始 content provider的學習
複習四大 組件content providerservicebroadcast provideractvity
本章 overview
4.07 init code for content provider
- 測試打樁函數就佔了大半
- 然後傳統的函數裡面修改了 weatherContrace
- 增加了 weatherProvider
- 修改了 fetchweatherTask函數,代碼的來源是
從 forececastFragment代碼拆解出來的。
- 除了修改weatherContrace,其他檔案都是 新增加的
關鍵詞 uri
weatherContract裡面增加的最多就是產生uri的函數
/* Inner class that defines the table contents of the location table */ public static final class LocationEntry implements BaseColumns { ... public static Uri buildLocationUri(long id) { return ContentUris.withAppendedId(CONTENT_URI, id); } } ===
/* Inner class that defines the table contents of the weather table */ public static final class WeatherEntry implements BaseColumns { ... public static Uri buildWeatherLocation(String locationSetting) { return null; } public static Uri buildWeatherLocationWithStartDate( String locationSetting, long startDate) { long normalizedDate = normalizeDate(startDate); return CONTENT_URI.buildUpon().appendPath(locationSetting) .appendQueryParameter(COLUMN_DATE, Long.toString(normalizedDate)).build(); } public static Uri buildWeatherLocationWithDate(String locationSetting, long date) { return CONTENT_URI.buildUpon().appendPath(locationSetting) .appendPath(Long.toString(normalizeDate(date))).build(); } public static String getLocationSettingFromUri(Uri uri) { return uri.getPathSegments().get(1); } public static long getDateFromUri(Uri uri) { return Long.parseLong(uri.getPathSegments().get(2)); } public static long getStartDateFromUri(Uri uri) { String dateString = uri.getQueryParameter(COLUMN_DATE); if (null != dateString && dateString.length() > 0) return Long.parseLong(dateString); else return 0; } }}
4.08 uri location with weather
僅2處更新
1/2 weatherContract
/* Student: This is the buildWeatherLocation function you filled in. */ public static Uri buildWeatherLocation(String locationSetting) { return CONTENT_URI.buildUpon().appendPath(locationSetting).build();//上一版本這裡返回空值 }
##2/2 test
僅僅開啟 這裡的注釋
“` java
/*
Students: This is NOT a complete test for the WeatherContract — just for the functions
that we expect you to write.
*/
public class TestWeatherContract extends AndroidTestCase {
// intentionally includes a slash to make sure Uri is getting quoted correctlyprivate static final String TEST_WEATHER_LOCATION = "/North Pole";private static final long TEST_WEATHER_DATE = 1419033600L; // December 20th, 2014/* Students: Uncomment this out to test your weather location function. */public void testBuildWeatherLocation() { Uri locationUri = WeatherContract.WeatherEntry.buildWeatherLocation(TEST_WEATHER_LOCATION); assertNotNull("Error: Null Uri returned. You must fill-in buildWeatherLocation in " + "WeatherContract.", locationUri); assertEquals("Error: Weather location not properly appended to the end of the Uri", TEST_WEATHER_LOCATION, locationUri.getLastPathSegment()); assertEquals("Error: Weather location Uri doesn‘t match our expected result", locationUri.toString(), "content://com.example.android.sunshine.app/weather/%2FNorth%20Pole");}
}
“`
4.09 write uri matcher1/2 weatherProvider
這是架構裡面的代碼,更新一處
/* Students: Here is where you need to create the UriMatcher. This UriMatcher will match each URI to the WEATHER, WEATHER_WITH_LOCATION, WEATHER_WITH_LOCATION_AND_DATE, and LOCATION integer constants defined above. You can test this by uncommenting the testUriMatcher test within TestUriMatcher. */ static UriMatcher buildUriMatcher() { // I know what you‘re thinking. Why create a UriMatcher when you can use regular // expressions instead? Because you‘re not crazy, that‘s why. // All paths added to the UriMatcher have a corresponding code to return when a match is // found. The code passed into the constructor represents the code to return for the root // URI. It‘s common to use NO_MATCH as the code for this case. final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); final String authority = WeatherContract.CONTENT_AUTHORITY; // For each type of URI you want to add, create a corresponding code. matcher.addURI(authority, WeatherContract.PATH_WEATHER, WEATHER); matcher.addURI(authority, WeatherContract.PATH_WEATHER + "/*", WEATHER_WITH_LOCATION); matcher.addURI(authority, WeatherContract.PATH_WEATHER + "/*/#", WEATHER_WITH_LOCATION_AND_DATE); matcher.addURI(authority, WeatherContract.PATH_LOCATION, LOCATION); return matcher; }
2/2 test
依然是已經寫好的測試代碼架構,這裡僅僅開啟注釋
public class TestUriMatcher extends AndroidTestCase {... /* Students: This function tests that your UriMatcher returns the correct integer value for each of the Uri types that our ContentProvider can handle. Uncomment this when you are ready to test your UriMatcher. */ public void testUriMatcher() { UriMatcher testMatcher = WeatherProvider.buildUriMatcher(); assertEquals("Error: The WEATHER URI was matched incorrectly.", testMatcher.match(TEST_WEATHER_DIR), WeatherProvider.WEATHER); assertEquals("Error: The WEATHER WITH LOCATION URI was matched incorrectly.", testMatcher.match(TEST_WEATHER_WITH_LOCATION_DIR), WeatherProvider.WEATHER_WITH_LOCATION); assertEquals("Error: The WEATHER WITH LOCATION AND DATE URI was matched incorrectly.", testMatcher.match(TEST_WEATHER_WITH_LOCATION_AND_DATE_DIR), WeatherProvider.WEATHER_WITH_LOCATION_AND_DATE); assertEquals("Error: The LOCATION URI was matched incorrectly.", testMatcher.match(TEST_LOCATION_DIR), WeatherProvider.LOCATION); }
···
4.10 register content provider1/2
這裡僅僅使能(開啟注釋) testProviderRegistry
public class TestProvider extends AndroidTestCase { ... /* This test checks to make sure that the content provider is registered correctly. Students: Uncomment this test to make sure you‘ve correctly registered the WeatherProvider. */ public void testProviderRegistry() { PackageManager pm = mContext.getPackageManager(); // We define the component name based on the package name from the context and the // WeatherProvider class. ComponentName componentName = new ComponentName(mContext.getPackageName(), WeatherProvider.class.getName()); try { // Fetch the provider info using the component name from the PackageManager // This throws an exception if the provider isn‘t registered. ProviderInfo providerInfo = pm.getProviderInfo(componentName, 0); // Make sure that the registered authority matches the authority from the Contract. assertEquals("Error: WeatherProvider registered with authority: " + providerInfo.authority + " instead of authority: " + WeatherContract.CONTENT_AUTHORITY, providerInfo.authority, WeatherContract.CONTENT_AUTHORITY); } catch (PackageManager.NameNotFoundException e) { // I guess the provider isn‘t registered correctly. assertTrue("Error: WeatherProvider not registered at " + mContext.getPackageName(), false); } } }
androidManifest update
<provider android:authorities="com.example.android.sunshine.app" android:name=".data.WeatherProvider" />
小憩
增加provider布湊
- 估計要從 1代版本代碼 和 2代代碼 進行比較
聰明的編譯器
智能提示
android 在debug模式下,有很有趣的話題在裡面,比如變數值的提示
還有搜尋字元時,主動過濾注釋裡面字元
test ok 單元測試的方法 && testPractise
並且 我沒有選右鍵觸發,而是直接 run ( shift f10)
(4a-28 )裡面單元測試的方法,完全可以複現
- 展現啟動並執行配置
- 展示出錯情形如何定位錯誤
- 展示正常情況
使用的是 sunshine version2 的4.02的代碼,code: testPractise
todo 實踐後思考空白的android test工程是怎樣的?4.02章節裡面,命名只有有效3個testcase,怎麼顯示有6個testcase ?
testDb之testCreateDb
4.04 (git version) delete the testPractise,
實際運行記錄:我在所有的 assertTrue,assertFalse,assertEqual位置都下了斷點,但是只有testDb中的testCreateDb 被觸發,數字上,run視窗顯示的“6 of 6”ok,但6個中的4個被觸發,我懷疑這個6是不是顯示過時
testdb之testLocationTable
測試完全通過.
並且有趣的是調用了TestUtilities.validateCurrentRecord。
(這裡面還有2個assert,這裡面的斷言終於被觸發了)
這是我觀察中
testUtilities類中函數的第一次調用,
上一輪,我在這個類的static函數裡面設定斷點,
都沒有被調用。
還有這個類testUtilities提供了資料庫插入的樣本資料
todo實踐後思考:既然單元測試都是利用AndroidTestCase那麼為什麼每次都是testDb被調用,而其他AndroidTestCase的繼承類testUtilities 沒有被直接調用.
testWeatherTable 和重構 testLocationTable
4.06 lesson,
而且有趣的是前者內部調用調用了
locationTable的內容
竟然所有的測試通過了
todo 內容深入分析 // Fifth Step: Validate the location Query TestUtilities.validateCurrentRecord("testInsertReadDb weatherEntry failed to validate", weatherCursor, weatherValues);
小憩
github 前6個小節,就是認識單元測試
單元測試的魔力很大啊,這裡
後續
from 4.06 -4.10 (github)
測試函數有:
- test weatherContract
- test uri matcher
- test Provider
- test providerRegistery
構造一個content provider,每個模組都不能馬虎
晚安 小憩
至少方向是光明的
下一天 複習
解釋 前面 為什麼 不是 全部的uri都被命中
再看 weather provider 和 testProvider的示範
作者是逐一功能的enable,逐一測試。逐漸深入
幾乎每種testProvider的函數(query/getType/insert/updat)
都含有uri matcher的篩選
新近發現 可以 查看 每一個測試的內容:
video 4b-24 是個階段總結
bulkinsert 其實利用了事務,只有endtrancsation 才執行操作
小節
小節:整章節 就是 uri 使用
- define uri
- update uri(feflect to number)
- uri matcher (I think ,here addmatch)
- implement (I think, sUriMatcher.match(uri);)
說到,開源很多 封裝了 content provider ? toco
如何理解 notification oserver ..here
整合
至此,我們的content provider 還沒有和 database 聯合起來。
操作不是 test*.java,也不主要是 weatherprovider裡面
我們將精力集中到 FetchweatherTask
- addlocation
- getWeatherDataFromJson insert + query part
(網路收報後的處理常式)
看到凡是使用了getContentResolver的地方,都是利用了contentprovider的地方,
最後
為了ui theread 更加流暢,引出Loader
至此,我們的video 來到4b的借書,代碼
代碼範圍是:
todo 附code小節
android 你好
android 再見
呵呵,本部落格android暫停更新,如果有興趣可以簡訊聯絡
udacity android 實踐筆記: lesson 4 part b