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 (specifically ACTION_TECH_DISCOVERED
) and put the given tag (in NfcAdapter.EXTRA_TAG
) on a bus system. However to my surprise I cannot create a Tag
instance or mock one. Can someone explain to me how I can (unit) test?
At this point I would even accept a form of integration testing, the process of:
Tag
objectCardDetectedEvent
.I have a phone with NFC enabled and a few cards for testing.
Android SDK version: 19
Libraries used: robolectric, junit and mockito
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).
Get the createMockTag()
method though reflection:
Class tagClass = Tag.class;
Method createMockTagMethod = tagClass.getMethod("createMockTag", byte[].class, int[].class, Bundle[].class);
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)
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
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 };
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With