ฉันสามารถใช้การยืนยันบนอุปกรณ์ Android ได้หรือไม่


88

ฉันต้องการใช้คีย์เวิร์ดAssertในแอพ Android ของฉันเพื่อทำลายแอพของฉันในบางกรณีบนโปรแกรมจำลองหรืออุปกรณ์ของฉันในระหว่างการทดสอบ เป็นไปได้หรือไม่

ดูเหมือนว่าโปรแกรมจำลองจะไม่สนใจคำยืนยันของฉัน


2
สำหรับ ART แทนที่จะเป็น Dalvik โปรดดูstackoverflow.com/questions/35997703/…
fadden

1
โปรดทราบว่าคำตอบที่ยอมรับนั้นค่อนข้างทำให้เข้าใจผิด
ลดกิจกรรม

คำตอบ:


-7

API ที่ให้JUnit ยืนยัน

คุณทำได้

import static junit.framework.Assert.*;

ตอนนี้คุณสามารถใช้ฟังก์ชันทั้งหมดเช่น assertTrue, assertEquals, assertNull ที่มีให้ในกรอบงาน junit

ระวังอย่าอิมพอร์ตเฟรมเวิร์ก Junit4 ผ่าน eclipse ซึ่งจะเป็นแพ็กเกจ org.junit คุณต้องใช้แพ็คเกจ junit.framework เพื่อให้มันทำงานบนอุปกรณ์ Android หรือโปรแกรมจำลอง


51
OP ขอ "ยืนยันคำหลัก" ซึ่งไม่เหมือน junit.framework สามารถปรับค่า JIT ให้เหมาะสมได้ และด้วยเหตุนี้ฉันจึงมาที่นี่ หวังว่าคำตอบอื่น ๆ จะเป็นประโยชน์มากขึ้น
Martin

27
ฉันเกลียดที่จะพูดหยาบคาย แต่นี่ไม่ควรเป็นคำตอบที่ยอมรับได้เพราะมันไม่ตอบคำถาม (ฉันเห็นด้วยกับความคิดเห็นของ @Martin) คำตอบอื่น ๆ อธิบายวิธีทำให้ฟังก์ชันคำหลักยืนยันถูกต้องเช่นเรียกใช้ "adb shell setprop debug.assert 1"
jfritz42

3
โหวตลดลงเนื่องจาก OP ถามเกี่ยวกับการยืนยันคำหลัก @scorpiodawg ได้ระบุกระบวนการไว้ด้านล่าง: stackoverflow.com/a/5563637/484261

คำตอบของ scorpiodawg มาเพียงหนึ่งปีต่อมาดังนั้นฉันเดาว่าคำตอบนี้ได้รับการยอมรับเพียงเพราะ OP ด้วยเหตุผลบางประการรู้สึกว่าจำเป็นต้องทำเครื่องหมายคำตอบว่ายอมรับ ทัศนคตินี้ทำให้คำตอบจำนวนมากเกี่ยวกับ SO ไม่สมบูรณ์หรือแย่มาก
async

145

ดูเอกสาร Embedded VM Control (HTML ดิบจากแผนผังต้นทางหรือสำเนาที่มีรูปแบบสวยงาม )

โดยทั่วไป Dalvik VM ถูกตั้งค่าให้ละเว้นการตรวจสอบการยืนยันตามค่าเริ่มต้นแม้ว่ารหัสไบต์. dex จะมีรหัสสำหรับทำการตรวจสอบก็ตาม การตรวจสอบการยืนยันเปิดอยู่ด้วยหนึ่งในสองวิธี:

(1) โดยตั้งค่าคุณสมบัติระบบ "debug.assert" ผ่าน:

adb shell setprop debug.assert 1

ซึ่งฉันตรวจสอบแล้วว่าใช้งานได้ตามที่ตั้งใจไว้ตราบเท่าที่คุณติดตั้งแอปของคุณใหม่หลังจากทำสิ่งนี้หรือ

(2) โดยการส่งอาร์กิวเมนต์บรรทัดคำสั่ง "--enable-assert" ไปยัง dalvik VM ซึ่งอาจไม่ใช่สิ่งที่นักพัฒนาแอปสามารถทำได้ (มีใครแก้ไขฉันได้ถ้าฉันผิดที่นี่)

โดยทั่วไปมีแฟล็กที่สามารถตั้งค่าได้ทั้งแบบทั่วโลกในระดับแพ็กเกจหรือในระดับคลาสซึ่งเปิดใช้งานการยืนยันในระดับนั้น ๆ แฟล็กถูกปิดโดยค่าเริ่มต้นซึ่งเป็นผลมาจากการที่การตรวจสอบการยืนยันถูกข้ามไป

ฉันเขียนโค้ดต่อไปนี้ในกิจกรรมตัวอย่างของฉัน:


public class AssertActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    int x = 2 + 3;
    assert x == 4;
  }
}

สำหรับรหัสนี้รหัส dalvik byte ที่สร้างขึ้นคือ (สำหรับ Android 2.3.3):


// Static constructor for the class
000318:                                        |[000318] com.example.asserttest.AssertActivity.:()V
000328: 1c00 0300                              |0000: const-class v0, Lcom/example/asserttest/AssertActivity; // class@0003
00032c: 6e10 0c00 0000                         |0002: invoke-virtual {v0}, Ljava/lang/Class;.desiredAssertionStatus:()Z // method@000c
000332: 0a00                                   |0005: move-result v0
000334: 3900 0600                              |0006: if-nez v0, 000c // +0006
000338: 1210                                   |0008: const/4 v0, #int 1 // #1
00033a: 6a00 0000                              |0009: sput-boolean v0, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000
00033e: 0e00                                   |000b: return-void
000340: 1200                                   |000c: const/4 v0, #int 0 // #0
000342: 28fc                                   |000d: goto 0009 // -0004

: :

// onCreate() 00035c: |[00035c] com.example.asserttest.AssertActivity.onCreate:(Landroid/os/Bundle;)V 00036c: 6f20 0100 3200 |0000: invoke-super {v2, v3}, Landroid/app/Activity;.onCreate:(Landroid/os/Bundle;)V // method@0001 000372: 1501 037f |0003: const/high16 v1, #int 2130903040 // #7f03 000376: 6e20 0500 1200 |0005: invoke-virtual {v2, v1}, Lcom/example/asserttest/AssertActivity;.setContentView:(I)V // method@0005 00037c: 1250 |0008: const/4 v0, #int 5 // #5 00037e: 6301 0000 |0009: sget-boolean v1, Lcom/example/asserttest/AssertActivity;.$assertionsDisabled:Z // field@0000 000382: 3901 0b00 |000b: if-nez v1, 0016 // +000b 000386: 1251 |000d: const/4 v1, #int 5 // #5 000388: 3210 0800 |000e: if-eq v0, v1, 0016 // +0008 00038c: 2201 0c00 |0010: new-instance v1, Ljava/lang/AssertionError; // class@000c 000390: 7010 0b00 0100 |0012: invoke-direct {v1}, Ljava/lang/AssertionError;.:()V // method@000b 000396: 2701 |0015: throw v1 000398: 0e00 |0016: return-void

สังเกตว่าคอนสตรัคเตอร์แบบคงที่เรียกใช้เมธอด neededAssertionStatus บนวัตถุคลาสและตั้งค่าตัวแปร $ assertionsDisabled โปรดสังเกตด้วยว่าใน onCreate () โค้ดทั้งหมดที่จะโยน java.lang.AssertionError จะถูกคอมไพล์ แต่การดำเนินการนั้นขึ้นอยู่กับค่าของ $ assertionsDisabled ซึ่งถูกตั้งค่าสำหรับอ็อบเจ็กต์ Class ในตัวสร้างแบบคงที่

ดูเหมือนว่าคลาส Assert ของ JUnit เป็นสิ่งที่ใช้เป็นส่วนใหญ่ดังนั้นจึงน่าจะเป็นการเดิมพันที่ปลอดภัยที่จะใช้สิ่งนั้น ความยืดหยุ่นของคีย์เวิร์ดที่ยืนยันคือความสามารถในการเปิดการยืนยันในช่วงเวลาการพัฒนาและปิดคีย์เวิร์ดสำหรับการจัดส่งบิตและล้มเหลวอย่างสง่างาม

หวังว่านี่จะช่วยได้


ดูเหมือนว่า Google ได้ลบแหล่งที่มาที่สามารถเรียกดูได้ (ฉันเห็นการอ้างอิงถึงสิ่งนี้ในคำตอบของคำถามอื่น ๆ ที่นี่) ทางออกที่ดีที่สุดของคุณคือหาแหล่งที่มาหรือพยายามค้นหาสิ่งนี้ในเครื่องมือค้นหา ค้นหา "dalvik / embed-vm-control.html" นี่คือสถานที่หนึ่งเลยก็ว่าได้: assembla.com/code/android-gb-for-sharp-is01/git/nodes/dalvik/... หวังว่านี่จะช่วยได้
scorpiodawg

มีประโยชน์มากขอบคุณ ฉันสับสนกับความแตกต่างที่เกิดขึ้นในย่อหน้าที่สองถึงสุดท้าย เรากำลังพูดถึงการยืนยันสองประเภท: วิธีแรกคือวิธีการของแพ็คเกจ JUnit Assert เช่น assertNotNull () และที่สองคือคำหลัก 'ยืนยัน' ภาษา Java คำตอบของคุณใช้ได้กับทั้งสองอย่างหรือไม่? ตัวอย่างเช่นถ้าฉันimport static junit.framework.Assert.*แล้วใช้วิธีใดวิธีหนึ่งเช่นassertNotNull("It's null!", someObject);การยืนยันนี้ถูกปิดในการจัดส่งบิตหรือไม่
Jeffro

1
สวัสดี Jeffro ฉันไม่เชื่อเช่นนั้น - junit.framework.Assert เป็นเพียงคลาสที่แสดงข้อยกเว้นเมื่อพบว่าเงื่อนไขการป้อนข้อมูลเป็นเท็จ ในทางกลับกันคำหลักยืนยันถูกสร้างขึ้นในภาษา หวังว่านี่จะช่วยได้
scorpiodawg

3
มีวิธีทำadb shell setprop debug.assert 1ใน Eclipse หรือไม่?
Pacerier

1
การยืนยันสามารถทำได้จากเทอร์มินัลที่ทำงานบนอุปกรณ์หากคุณรูท เป็นครั้งแรกแล้วsu setprop debug.assert 1โปรดทราบว่ารหัสที่คุณแสดงการแยกชิ้นส่วนจะยังคงอยู่ในรุ่นรุ่น ( stackoverflow.com/a/5590378/506073 ) ฉันไม่เชื่อว่าคอมไพเลอร์ javac สามารถบอกได้ว่าจะไม่ปล่อยคำยืนยันดังนั้นพวกเขาจึงจำเป็นต้องถูกถอดออก วิธีแก้ปัญหาง่ายๆคือการรวมคำหลักที่ยืนยันในฟังก์ชันของคุณเองที่ proguard สามารถตัดให้คุณ
ahcox

10

เมื่อยืนยันจะเปิดใช้งานassertคำหลักเพียงแค่พ่นเมื่อนิพจน์บูลีนคือAssertionErrorfalse

ดังนั้น IMO ซึ่งเป็นทางเลือกที่ดีที่สุดโดยเฉพาะ หากคุณไม่ชอบที่จะพึ่งพาจูนิทก็คือการโยนAssertionErrorอย่างชัดเจนดังที่แสดงด้านล่าง:

assert x == 0 : "x = " + x;

อีกทางเลือกหนึ่งสำหรับข้อความข้างต้นคือ:

Utils._assert(x == 0, "x = " + x);

โดยวิธีการกำหนดเป็น:

public static void _assert(boolean condition, String message) {
    if (!condition) {
        throw new AssertionError(message);
    }
}

Oracle java docs แนะนำให้โยนAssertionErrorเป็นทางเลือกที่ยอมรับได้

ฉันเดาว่าคุณสามารถกำหนดค่า Proguard เพื่อตัดสายเหล่านี้ออกสำหรับรหัสการผลิต


แต่คุณจะเปิดใช้การยืนยันได้อย่างไร บน Android Studio?
SMBiggs

8

ใน "Android in Practice" แนะนำให้ใช้:

$adb shell setprop dalvik.vm.enableassertions all

หากการตั้งค่านี้ไม่คงอยู่ในโทรศัพท์ของคุณคุณสามารถสร้างไฟล์ /data/local.prop ด้วยคุณสมบัติเช่น:

dalvik.vm.enableassertions=all

ในstackoverflow.com/a/18556839/2004714คุณอาจต้องตรวจสอบให้แน่ใจว่าไฟล์นั้นเป็นแบบอ่านอย่างเดียว ( chmod 644)
Paulo

5

มันทำให้ฉันรู้สึกแย่มากที่การยืนยันของฉันไม่ได้ผลจนกว่าฉันจะตรวจสอบปัญหาใน google ... ฉันยอมแพ้กับการยืนยันง่ายๆและจะใช้วิธีการยืนยันแบบ Junits

เพื่อความสะดวกฉันใช้:

นำเข้า junit.framework.Assert แบบคงที่ *;

เนื่องจากการนำเข้าแบบคงที่ฉันสามารถเขียนได้ในภายหลัง:

assertTrue (... ); แทน Assert.assertTrue (... );


4

หากคุณกังวลเกี่ยวกับรหัสการจัดส่งที่มีการยืนยัน JUnit ใน (หรือพา ธ คลาสอื่น ๆ ) คุณสามารถใช้ตัวเลือกการกำหนดค่า ProGuard 'สมมติฐานผลกระทบ' ซึ่งจะตัดเส้นทางของคลาสออกโดยสมมติว่าการลบออกจะไม่ทำอะไรกับโค้ด .

เช่น.

-assumenosideeffects junit.framework.Assert {
*;
}

ฉันมีไลบรารีดีบักทั่วไปที่ฉันใส่วิธีการทดสอบทั้งหมดแล้วใช้ตัวเลือกนี้เพื่อดึงมันออกจากแอพที่ออก

นอกจากนี้ยังขจัดปัญหาที่ยากต่อการสังเกตเห็นของสตริงที่ถูกจัดการที่ไม่เคยใช้ในรหัสรุ่น ตัวอย่างเช่นถ้าคุณเขียนวิธีการบันทึกการดีบักและในวิธีนั้นคุณตรวจสอบโหมดดีบักก่อนที่จะบันทึกสตริงคุณยังคงสร้างสตริงจัดสรรหน่วยความจำเรียกใช้เมธอด แต่จากนั้นเลือกที่จะไม่ทำอะไรเลย การถอดคลาสออกจากนั้นจะลบการโทรออกทั้งหมดหมายความว่าตราบใดที่สตริงของคุณถูกสร้างขึ้นภายในการเรียกเมธอดมันก็จะหายไปเช่นกัน

ตรวจสอบให้แน่ใจว่ามีความปลอดภัยอย่างแท้จริงในการตัดเส้นออกอย่างไรก็ตามเนื่องจากไม่มีการตรวจสอบในส่วนของ ProGuard การลบวิธีการคืนค่าที่เป็นโมฆะจะใช้ได้ดีอย่างไรก็ตามหากคุณใช้ค่าส่งคืนใด ๆ จากสิ่งที่คุณกำลังลบตรวจสอบให้แน่ใจว่าคุณไม่ได้ใช้มันสำหรับตรรกะในการปฏิบัติงานจริง


1
ฉันคิดว่าไวยากรณ์ที่เหมาะสมจะเป็น:-assumenosideeffects class junit.framework.Assert { *; }
ปุ๊ก

คำตอบที่สับสน คำสั่งที่กล่าวถึงทำให้เกิดข้อผิดพลาด proguard คำสั่งที่แก้ไขโดย Pooks ยังคงไม่ลบการยืนยันออกจากไฟล์ binary dex
Pointer Null

ฉันมีปัญหาเดียวกันกับ @PointerNull ยืนยันว่าจะไม่ลบออก
Mahdi

3

คุณสามารถใช้การยืนยันได้ แต่ต้องใช้เวลาพอสมควรในการใช้อย่างน่าเชื่อถือ คุณสมบัติของระบบdebug.assertไม่น่าเชื่อถือ เห็นปัญหา175697 , 65183 , 36786และ17324

วิธีหนึ่งคือการแปลแต่ละassertคำสั่งเป็นสิ่งที่รันไทม์สามารถจัดการได้ ดำเนินการนี้กับพรีโปรเซสเซอร์ซอร์สด้านหน้าคอมไพเลอร์ Java ตัวอย่างเช่นใช้คำสั่งนี้:

assert x == 0: "Failure message";

สำหรับบิวด์ดีบักตัวประมวลผลก่อนของคุณจะแปลข้างต้นเป็นifคำสั่ง:

{ if( !(x == 0) ) throw new AssertionError( "Failure message" ); }

สำหรับบิลด์การผลิตไปยังคำสั่งว่าง:

;

โปรดทราบว่าสิ่งนี้จะควบคุมการยืนยันในเวลาสร้างซึ่งต่างจากเวลาทำงาน (การปฏิบัติตามปกติ)

ฉันไม่พบตัวประมวลผลล่วงหน้าสำเร็จรูปดังนั้นฉันจึงเขียนสคริปต์ขึ้นมา ดูส่วนที่เกี่ยวข้องกับการยืนยัน อนุญาตให้คัดลอกที่นี่


1

หากต้องการเพิ่มคำตอบของ Zulaxia เกี่ยวกับการแยก Junit ออก - Proguard เป็นส่วนหนึ่งของ Android SDK / Eclipse อยู่แล้วและหน้าต่อไปนี้จะบอกวิธีเปิดใช้งาน

http://developer.android.com/guide/developing/tools/proguard.html

นอกจากนี้ข้างต้นจะใช้ไม่ได้กับการกำหนดค่า proguard เริ่มต้นล่าสุดเนื่องจากใช้แฟล็ก -dontoptimize ซึ่งจะต้องถูกนำออกและการเพิ่มประสิทธิภาพบางอย่างเปิดอยู่


0

ใช้คีย์เวิร์ดJava assertมาตรฐานตัวอย่างเช่น:

assert a==b;

เพื่อให้ได้ผลคุณต้องเพิ่มหนึ่งบรรทัดใน /system/build.prop และรีบูตโทรศัพท์:

debug.assert=1

สิ่งนี้จะใช้ได้กับโทรศัพท์ที่รูท ใช้ตัวจัดการไฟล์บางตัวที่สามารถแก้ไข build.prop (เช่น X-plore)

ข้อดี: โทรศัพท์ Android ส่วนใหญ่ (ทั้งหมด?) มาพร้อมกับปิดใช้งานการยืนยัน แม้ว่ารหัสของคุณจะยืนยันว่าเป็นเท็จโดยไม่ตั้งใจ แต่แอปจะไม่ขัดจังหวะหรือขัดข้อง อย่างไรก็ตามบนอุปกรณ์การพัฒนาของคุณคุณจะได้รับข้อยกเว้นในการยืนยัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.