如何模拟Android NFC Tag对象以进行单元测试

Jaa*_*ans 6 android unit-testing mocking nfc android-intent

我目前正在开发一个需要NFC集成的Android项目.现在,我想编写一些(j)单元测试,以查看应用程序是否可以接收NFC意图(特别是ACTION_TECH_DISCOVERED)并将给定标记(in NfcAdapter.EXTRA_TAG)放在总线系统上.但令我惊讶的是,我无法创建Tag实例或模拟实例.有人可以向我解释我是如何(单位)测试的吗?

此时我甚至会接受一种集成测试,其过程如下:

  1. 检测NFC意图
  2. 获取Tag对象
  3. 把它放在包裹着的公共汽车上CardDetectedEvent.

我有一部支持NFC的手机和一些用于测试的卡.

Android SDK版本:19
使用的库:robolectric,junit和mockito

Mic*_*and 8

可以使用反射创建模拟标记对象实例(请注意,这不是公共Android SDK的一部分,因此未来的Android版本可能会失败).

  1. createMockTag()通过反射获取方法:

    Class tagClass = Tag.class;
    Method createMockTagMethod = tagClass.getMethod("createMockTag", byte[].class, int[].class, Bundle[].class);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 定义一些用于准备模拟标记实例的常量:

    final int TECH_NFC_A = 1;
    final String EXTRA_NFC_A_SAK = "sak";    // short (SAK byte value)
    final String EXTRA_NFC_A_ATQA = "atqa";  // byte[2] (ATQA value)
    
    final int TECH_NFC_B = 2;
    final String EXTRA_NFC_B_APPDATA = "appdata";    // byte[] (Application Data bytes from ATQB/SENSB_RES)
    final String EXTRA_NFC_B_PROTINFO = "protinfo";  // byte[] (Protocol Info bytes from ATQB/SENSB_RES)
    
    final int TECH_ISO_DEP = 3;
    final String EXTRA_ISO_DEP_HI_LAYER_RESP = "hiresp";  // byte[] (null for NfcA)
    final String EXTRA_ISO_DEP_HIST_BYTES = "histbytes";  // byte[] (null for NfcB)
    
    final int TECH_NFC_F = 4;
    final String EXTRA_NFC_F_SC = "systemcode";  // byte[] (system code)
    final String EXTRA_NFC_F_PMM = "pmm";        // byte[] (manufacturer bytes)
    
    final int TECH_NFC_V = 5;
    final String EXTRA_NFC_V_RESP_FLAGS = "respflags";  // byte (Response Flag)
    final String EXTRA_NFC_V_DSFID = "dsfid";           // byte (DSF ID)
    
    final int TECH_NDEF = 6;
    final String EXTRA_NDEF_MSG = "ndefmsg";              // NdefMessage (Parcelable)
    final String EXTRA_NDEF_MAXLENGTH = "ndefmaxlength";  // int (result for getMaxSize())
    final String EXTRA_NDEF_CARDSTATE = "ndefcardstate";  // int (1: read-only, 2: read/write, 3: unknown)
    final String EXTRA_NDEF_TYPE = "ndeftype";            // int (1: T1T, 2: T2T, 3: T3T, 4: T4T, 101: MF Classic, 102: ICODE)
    
    final int TECH_NDEF_FORMATABLE = 7;
    
    final int TECH_MIFARE_CLASSIC = 8;
    
    final int TECH_MIFARE_ULTRALIGHT = 9;
    final String EXTRA_MIFARE_ULTRALIGHT_IS_UL_C = "isulc";  // boolean (true: Ultralight C)
    
    final int TECH_NFC_BARCODE = 10;
    final String EXTRA_NFC_BARCODE_BARCODE_TYPE = "barcodetype";  // int (1: Kovio/ThinFilm)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 为您的标记类型创建tech-extras包.例如,对于带有NDEF消息的NFC-A标签:

    Bundle nfcaBundle = new Bundle();
    nfcaBundle.putByteArray(EXTRA_NFC_A_ATQA, new byte[]{ (byte)0x44, (byte)0x00 }); //ATQA for Type 2 tag
    nfcaBundle.putShort(EXTRA_NFC_A_SAK , (short)0x00); //SAK for Type 2 tag
    
    Bundle ndefBundle = new Bundle();
    ndefBundle.putInt(EXTRA_NDEF_MAXLENGTH, 48); // maximum message length: 48 bytes
    ndefBundle.putInt(EXTRA_NDEF_CARDSTATE, 1); // read-only
    ndefBundle.putInt(EXTRA_NDEF_TYPE, 2); // Type 2 tag
    NdefMessage myNdefMessage = ...; // create an NDEF message
    ndefBundle.putParcelable(EXTRA_NDEF_MSG, myNdefMessage);  // add an NDEF message
    
    Run Code Online (Sandbox Code Playgroud)
  4. 为您的标签准备防冲突标识符/ UID(请参阅Tag.getId()方法).例如,Type 2标签的7字节UID:

    byte[] tagId = new byte[] { (byte)0x3F, (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte)0x90, (byte)0xAB };
    
    Run Code Online (Sandbox Code Playgroud)
  5. 然后,您可以通过调用该createMockTag()方法来创建模拟标记实例

    Tag mockTag = (Tag)createMockTagMethod.invoke(null,
            tagId,                                     // tag UID/anti-collision identifier (see Tag.getId() method)
            new int[] { TECH_NFC_A, TECH_NDEF },       // tech-list
            new Bundle[] { nfcaBundle, ndefBundle });  // array of tech-extra bundles, each entry maps to an entry in the tech-list
    
    Run Code Online (Sandbox Code Playgroud)

创建该模拟标记对象后,您可以将其作为NFC发现意图的一部分发送.例如,TECH_DISCOVERED意图:

Intent techIntent = new Intent(NfcAdapter.ACTION_TECH_DISCOVERED);
techIntent.putExtra(NfcAdapter.EXTRA_ID, tagId);
techIntent.putExtra(NfcAdapter.EXTRA_TAG, mockTag);
techIntent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[]{ myNdefMessage });  // optionally add an NDEF message
Run Code Online (Sandbox Code Playgroud)

然后,您可以将此意图发送到您的活动:

techIntent.setComponent(...); // or equivalent to optionally set an explicit receiver
startActivity(techIntent);
Run Code Online (Sandbox Code Playgroud)

接收器甚至可以使用模拟标记对象来检索技术类的实例.但是,任何需要IO操作的方法都将失败.

  • 有谁知道这段代码是否仍然适用于 SdkVersion >= 26?我收到以下错误: java.lang.NoSuchMethodException: android.nfc.Tag.createMockTag [class [B, class [I, class [Landroid.os.Bundle;] (2认同)