Digging into the Teensy Midi library's API put this loopback tester together. Code is based on the example in File>Examples>Teensy>USB_MIDI>TransmitEverything.

Running on a T3.5, USB Type MIDIx4, pressing the button a Midi message is sent concurrently on usbMIDI cable 0 and Serial 1 TX. Serial1 TX and RX are connected via 470 Ohm resistor. Callbacks are used to send looped messages via usbMIDI cable 1.
Here's its pic:-
Click image for larger version. 

Name:	T3.5_Loopback.jpg 
Views:	3 
Size:	89.1 KB 
ID:	23664
Compiled, uploaded with IDE1.8.13 TD 1.53 on Win7 or 10 and monitored with MidiOx, however there is a subtle difference in the behavior depending on the OS.

Works as expected on Win 10, however first time through on Win 7, messages are seen on both cables until the first Sysex message is sent then no messages are seen on either cable until the button has been pressed another eighteen times and we see note On and Off messages. Thereafter, behavior is as expected.

The contents of void sendNextMessage() line 91 show us the differences between the MIDI and usbMIDI API details.

Here's a snip of a screen snapshot from MidiOx. In 7 = cable 0 and In 8 = cable 1.

Click image for larger version. 

Name:	MidiOx_Loopback.jpg 
Views:	3 
Size:	67.0 KB 
ID:	23665

And here's its code:-

Code:
/*
   A pushbutton (ordinary momentary type) needs to be connected
   between pin 8 and GND. Serial1 TX and RX are connected with a
   470R resistor.

   You must select MIDIx4 from the "Tools > USB Type" menu

   Each time a button is pressed, send another usbMIDI message
   concurrently on the primaryCable and Serial1 TX.
   Serial1 RX is decoded and re-sent on loopedCable.

   This very long example demonstrates all possible usbMIDI
   and Serial Midi message send functions. It's mostly meant
   for testing and as a reference to easily copy-and-paste
   the code for every message send function.

   This example code is in the public domain.
*/
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI virtual cable to use
int primaryCable = 0;
int loopedCable  = 1;
// Create a Bounce object for the button.
const int pin = 47;
Bounce button1 = Bounce(pin, 10);

// remember when a note-on message has been sent
int note = 0;

// which message will we do next
int state = 0;

// sysex message to send
uint8_t buf[] = {0xF0, 6, 24, 64, 5, 1, 0xF7};

void setup() {
  pinMode(pin, INPUT_PULLUP);
  MIDI.begin();
  MIDI.turnThruOff();// To prevent Midi feedback

  MIDI.setHandleNoteOff(myNoteOff);
  MIDI.setHandleNoteOn(myNoteOn);
  MIDI.setHandleAfterTouchPoly(myAfterTouchPoly);
  MIDI.setHandleControlChange(myControlChange);
  MIDI.setHandleProgramChange(myProgramChange);
  MIDI.setHandleAfterTouchChannel(myAfterTouchChannel);
  MIDI.setHandlePitchBend(myPitchBend);
  MIDI.setHandleSystemExclusive(mySystemExclusive);
  MIDI.setHandleClock(myClock);
  MIDI.setHandleStart(myStart);
  MIDI.setHandleContinue(myContinue);
  MIDI.setHandleStop(myStop);
  MIDI.setHandleActiveSensing(myActiveSensing);
  MIDI.setHandleSystemReset(mySystemReset);
  MIDI.setHandleTimeCodeQuarterFrame(myTimeCodeQuarterFrame);
  MIDI.setHandleTuneRequest(myTuneRequest);
  MIDI.setHandleSongPosition(mySongPosition);
  MIDI.setHandleSongSelect(mySongSelect);
}

void loop() {
  while (MIDI.read()) {
    // controllers must call .read() to keep the queue clear even if they are not responding to MIDI
  }
  while (usbMIDI.read()) {
    // ignore incoming messages
  }

  button1.update();
  if (button1.fallingEdge()) {
    // when the button is pressed, send another message
    sendNextMessage();
  }
  if (button1.risingEdge()) {
    // when the button is release, send note off if we left a note on
    if (note > 0) {
      usbMIDI.sendNoteOff(note, 0, channel, primaryCable);
      MIDI.sendNoteOff(note, 0, channel);
      note = 0;
    }
  }
}

void sendNextMessage() {
  switch (state) {
    case 0:
      note = analogRead(A0) / 8;
      if (note == 0) note = 1;
      usbMIDI.sendNoteOn(note, 99, channel, primaryCable);
      MIDI.sendNoteOn(note, 99, channel);
      break;
    case 1:
      usbMIDI.sendAfterTouchPoly(65, 110, channel, primaryCable);
      MIDI.sendAfterTouch(65, 110, channel);
      break;
    case 2:
      usbMIDI.sendControlChange(7, 100, channel, primaryCable);
      MIDI.sendControlChange(7, 100, channel);
      break;
    case 3:
      usbMIDI.sendProgramChange(2, channel, primaryCable);
      MIDI.sendProgramChange(2, channel);
      break;
    case 4:
      usbMIDI.sendAfterTouch(108, channel, primaryCable);
      MIDI.sendAfterTouch(108, channel);
      break;
    case 5:
      usbMIDI.sendPitchBend(911, channel, primaryCable);
      MIDI.sendPitchBend(911, channel);
      break;
    case 6:
      usbMIDI.sendSysEx(sizeof(buf), buf, true, primaryCable);
      MIDI.sendSysEx(sizeof(buf), buf, true);
      break;
    case 7:
      usbMIDI.sendSysEx(sizeof(buf) - 2, buf + 1, false, primaryCable);
      MIDI.sendSysEx(sizeof(buf) - 2, buf + 1, false);
      break;
    case 8:
      usbMIDI.sendRealTime(usbMIDI.Clock, primaryCable);
      MIDI.sendClock();
      break;
    case 9:
      usbMIDI.sendRealTime(usbMIDI.Start, primaryCable);
      MIDI.sendStart();
      break;
    case 10:
      usbMIDI.sendRealTime(usbMIDI.Continue, primaryCable);
      MIDI.sendContinue();
      break;
    case 11:
      usbMIDI.sendRealTime(usbMIDI.Stop, primaryCable);
      MIDI.sendStop();
      break;
    case 12:
      usbMIDI.sendRealTime(usbMIDI.ActiveSensing, primaryCable);
      MIDI.sendActiveSensing();
      break;
    case 13:
      usbMIDI.sendRealTime(usbMIDI.SystemReset, primaryCable);
      MIDI.sendSystemReset();
      break;
    case 14:
      usbMIDI.sendTimeCodeQuarterFrame(0, 3, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(1, 1, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(2, 7, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(3, 4, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(4, 1, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(5, 1, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(6, 0, primaryCable);
      usbMIDI.sendTimeCodeQuarterFrame(7, 0, primaryCable);
      /*
       Commented out as these messages are actually sent
       on usbMIDI primaryCable
            MIDI.sendTimeCodeQuarterFrame(0, 3);
            MIDI.sendTimeCodeQuarterFrame(1, 1);
            MIDI.sendTimeCodeQuarterFrame(2, 7);
            MIDI.sendTimeCodeQuarterFrame(3, 4);
            MIDI.sendTimeCodeQuarterFrame(4, 1);
            MIDI.sendTimeCodeQuarterFrame(5, 1);
            MIDI.sendTimeCodeQuarterFrame(6, 0);
            MIDI.sendTimeCodeQuarterFrame(7, 0);
      */
      break;
    case 15:
      usbMIDI.sendTuneRequest(primaryCable);
      MIDI.sendTuneRequest();
      break;
    case 16:
      usbMIDI.sendSongPosition(2531, primaryCable);
      MIDI.sendSongPosition(2531);
      break;
    case 17:
      usbMIDI.sendSongSelect(107, primaryCable);
      MIDI.sendSongSelect(107);
      break;
    case 18:
      usbMIDI.beginRpn(1, channel, primaryCable);
      usbMIDI.sendRpnValue(6489, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginRpn(1, channel);
      MIDI.sendRpnValue(6489, channel);
      MIDI.endRpn(channel);
      break;
    case 19:
      usbMIDI.beginRpn(1, channel, primaryCable);
      usbMIDI.sendRpnIncrement(14, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginRpn(1, channel);
      MIDI.sendRpnIncrement(14, channel);
      MIDI.endRpn(channel);
      break;
    case 20:
      usbMIDI.beginRpn(1, channel, primaryCable);
      usbMIDI.sendRpnDecrement(9, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginRpn(1, channel);
      MIDI.sendRpnDecrement(9, channel);
      MIDI.endRpn(channel);
      break;
    case 21:
      usbMIDI.beginNrpn(417, channel, primaryCable);
      usbMIDI.sendRpnValue(6489, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginNrpn(417, channel);
      MIDI.sendRpnValue(6489, channel);
      MIDI.endRpn(channel);
      break;
    case 22:
      usbMIDI.beginNrpn(417, channel, primaryCable);
      usbMIDI.sendRpnIncrement(3, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginNrpn(417, channel);
      MIDI.sendRpnIncrement(3, channel);
      MIDI.endRpn(channel);
      break;
    case 23:
      usbMIDI.beginNrpn(417, channel, primaryCable);
      usbMIDI.sendRpnDecrement(2, channel, primaryCable);
      usbMIDI.endRpn(channel, primaryCable);

      MIDI.beginNrpn(417, channel);
      MIDI.sendRpnDecrement(2, channel);
      MIDI.endRpn(channel);
      break;
  }
  state = state + 1;
  if (state > 23) state = 0;
}
//*******************  Handlers  **************************************
void myNoteOff(byte channel, byte note, byte velocity) {
  usbMIDI.sendNoteOff(note, velocity, channel, loopedCable);
}
void myNoteOn(byte channel, byte note, byte velocity) {
  usbMIDI.sendNoteOn(note, velocity, channel, loopedCable);
}
void myAfterTouchPoly(byte channel, byte note, byte velocity) {
  usbMIDI.sendAfterTouchPoly(note, velocity, channel, loopedCable);
}
void myControlChange(byte channel, byte control, byte value) {
  usbMIDI.sendControlChange(control, value, channel, loopedCable);
}
void myProgramChange(byte channel, byte program) {
  usbMIDI.sendProgramChange(program, channel, loopedCable);
}
void myAfterTouchChannel(byte channel, byte pressure) {
  usbMIDI.sendAfterTouch(pressure, channel, loopedCable);
}
void myPitchBend(byte channel, int pitch) {
  usbMIDI.sendPitchBend(pitch, channel, loopedCable);
}
void mySystemExclusive(const byte *data, uint16_t length) {
  usbMIDI.sendSysEx( length, data, true, loopedCable);
}
void myClock() {
  usbMIDI.sendRealTime(usbMIDI.Clock, loopedCable);
}
void myStart() {
  usbMIDI.sendRealTime(usbMIDI.Start, loopedCable);
}
void myContinue() {
  usbMIDI.sendRealTime(usbMIDI.Continue, loopedCable);
}
void myStop() {
  usbMIDI.sendRealTime(usbMIDI.Stop, loopedCable);
}
void myActiveSensing() {
  usbMIDI.sendRealTime(usbMIDI.ActiveSensing, loopedCable);
}
void mySystemReset() {
  usbMIDI.sendRealTime(usbMIDI.SystemReset, loopedCable);
}
void myTimeCodeQuarterFrame(byte data) {
  usbMIDI.sendTimeCodeQuarterFrame(data,  loopedCable);
}
void myTuneRequest() {
  usbMIDI.sendTuneRequest(loopedCable);
}
// needed unsigned to remove compiler warning
void mySongPosition(unsigned beats) {
  usbMIDI.sendSongPosition(beats, loopedCable);
}
void mySongSelect(byte songNumber) {
  usbMIDI.sendSongSelect(songNumber, loopedCable);
}
WRT line 160, dunno if I've missed something obvious or what with Midi TimeCode, not needed to use it yet.