How to mock a Android NFC Tag object for unit testing

后端 未结 2 840
太阳男子
太阳男子 2020-12-16 03:16

I\'m currently working on an Android project which needs NFC integration. Now I want to write some (j)unit tests to see if the application can receive NFC intents (specifica

相关标签:
2条回答
  • 2020-12-16 03:29

    It's possible to create a mock tag object instance using reflection (note that this is not part of the public Android SDK, so it might fail for future Android versions).

    1. Get the createMockTag() method though reflection:

      Class tagClass = Tag.class;
      Method createMockTagMethod = tagClass.getMethod("createMockTag", byte[].class, int[].class, Bundle[].class);
      
    2. Define some constants for preparing the mock tag instance:

      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)
      
    3. Create the tech-extras bundle for your tag type. For instance, for an NFC-A tag with an NDEF message:

      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
      
    4. Prepare an anti-collision identifier/UID for your tag (see Tag.getId() method). E.g. a 7-byte-UID for a Type 2 tag:

      byte[] tagId = new byte[] { (byte)0x3F, (byte)0x12, (byte)0x34, (byte)0x56, (byte)0x78, (byte)0x90, (byte)0xAB };
      
    5. Then you can create a mock tag instance by invoking the createMockTag() method

      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
      

    Once you created that mock tag object, you can send it as part of an NFC discovery intent. E.g. for a TECH_DISCOVERED intent:

    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
    

    You can then send this intent to your activity:

    techIntent.setComponent(...); // or equivalent to optionally set an explicit receiver
    startActivity(techIntent);
    

    The receiver can even use the mock tag object to retrieve instances of the technology classes. However, any method that requires IO operations will fail.

    0 讨论(0)
  • 2020-12-16 03:47

    I don't think it is possible to mock these intents as these are fired by NFCService and without system permissions it is not possible to fire these intents and currently there is not support in the android framework to mock nfc tags.

    0 讨论(0)
提交回复
热议问题