Testen einer CarrierLookup-Integration offline mit Antwort-Fixtures
Testen Sie flache Carrier-Felder, leere Ergebnisse, geordnete Multi-Antworten und Fehlerzweige ohne echte Kontaktdaten oder kostenpflichtige API-Aufrufe.

Injizieren Sie den Transport und testen Sie explizite synthetische Antwort-Fixtures, bevor Sie einem CarrierLookup-Consumer den Zugriff auf das Netzwerk erlauben.
CarrierLookup API-Mock-Tests sollten den HTTP-Transport ersetzen, versehentlichen Netzwerkzugriff verweigern und sicherstellen, wie Ihre Anwendung mit jedem dokumentierten Ergebnis umgeht. Synthetische Fixtures können die Consumer-Logik testen, ohne Telefonnummern zu übermitteln oder Guthaben zu verbrauchen.
Platzieren Sie die Test-Schnittstelle vor dem HTTP-Anfrage
Ein CRM-Importer kann seinen Happy-Path-Test bestehen und dennoch eine unbestimmte Nummer als ungültig kennzeichnen. Er kann auch einen gemockten Browser-Test bestehen, während sein Backend eine echte kostenpflichtige Anfrage stellt. Platzieren Sie Ihre Test-Schnittstelle an der Backend-Funktion, die den ausgehenden HTTP-Austausch durchführt.
Der Consumer sollte eine Transport-Abhängigkeit akzeptieren, anstatt einen echten Client intern zu ermitteln. Der injizierte Test-Transport empfängt den beabsichtigten Pfad und den Anfrage-Body, validiert diese und gibt einen synthetischen Umschlag zurück. Es gibt keinen API-Schlüssel im Test. Halten Sie auch im CI-Job keine Live-Zugangsdaten bereit.
Verwenden Sie eine separate Netzwerkeinschränkung als Sicherheitsnetz. Ein Mock, der bequeme Daten zurückgibt, ist kein Beweis dafür, dass jeder ausgehende Anruf abgefangen wurde. Verweigern Sie ausgehenden Datenverkehr an der Grenze des CI-Containers oder Runners und lassen Sie den Job bei versuchtem Netzwerkzugriff fehlschlagen. Eine sprachbasierte Absicherung hilft dabei, häufige Fehler zu erkennen, kann jedoch keine Subprozesse oder jede Netzwerkbibliothek abdecken.
Definieren Sie die Ergebnisse vor dem Schreiben der Fixtures
Unser synchroner Carrier-Dienst verwendet service_type=carrier. Einzelne Antworten haben flache data-Felder, einschließlich identifier, carrier, underlying_carrier, number_type, country_code, region und city. Das Lesen von data.extra.carrier testet eine erfundene Struktur.
| Fixture | Consumer-Assertion |
|---|---|
| Code 0 mit Zuweisungsfeldern | Ursprüngliche Zuweisungswerte beibehalten |
| Code 0 mit allen Zuweisungsfeldern leer | Abgeschlossene Abfrage mit nicht verfügbaren Werten |
| Code 42200 | Unbestimmt; kein leeres Erfolgsergebnis erstellen |
| Multi-Element mit exists=false | Kein normales Ergebnis; die Nummer nicht als getrennt markieren |
| Code 42901 | Beschäftigt-Zweig; kein erfolgreiches CRM-Update |
| Code 50400 | Timeout-Zweig; kein erfolgreiches CRM-Update |
Carrier-Werte beschreiben die ursprüngliche Zuweisung. Sie sollten kein CRM-Feld namens aktuelles Netzwerk befüllen. Ebenso begründet weder ein leeres Zuweisungsergebnis noch exists=false die Gültigkeit einer Telefonnummer. Verwenden Sie separate Anwendungs-Ergebnis-Labels, damit eine spätere Abfrage diese Zustände nicht versehentlich zusammenführt.
Führen Sie eine kleine synthetische Fixture-Suite aus
Speichern Sie das Folgende als carrier_consumer_test.py und führen Sie python3 carrier_consumer_test.py aus. Es verwendet die Standardbibliothek von Python. Das Beispiel ist eine minimale Consumer-Schnittstelle mit synthetischen Identifikatoren und bewusst gewählten Werten; es kontaktiert niemals CarrierLookup. Ersetzen Sie den Demonstrations-client und den Klassifizierer durch Ihren eigenen Adapter, während Sie die Fixture-Assertionen beibehalten.
import socket
import unittest
from unittest.mock import patch
FIELDS = ("carrier", "underlying_carrier", "number_type",
"country_code", "region", "city")
A, B = "+12025550101", "+12025550102" # Nur synthetische Testdaten.
def classify(envelope):
code = envelope["code"]
if code != 0:
return {42200: "undetermined", 42901: "busy",
50400: "timeout"}.get(code, "error")
data = envelope["data"]
if data["service_type"] != "carrier":
raise ValueError("unexpected service")
return "completed" if data["carrier"] else "completed_empty"
def client(transport, identifiers):
multi = len(identifiers) > 1
path = "/api/v1/batch-check" if multi else "/api/v1/check"
body = {"service_type": "carrier"}
body["identifiers" if multi else "identifier"] = (
identifiers if multi else identifiers[0])
return transport(path, body)
def completed(identifier, carrier=""):
return {"code": 0, "msg": "synthetic fixture", "data": {
"service_type": "carrier", "identifier": identifier,
**dict.fromkeys(FIELDS, ""), "carrier": carrier}}
class ConsumerTests(unittest.TestCase):
def setUp(self):
self.guard = patch.object(
socket.socket, "connect",
side_effect=AssertionError("network forbidden"))
self.guard.start()
self.addCleanup(self.guard.stop)
def test_single_flat_data_and_outcomes(self):
for value, expected in [("Example Allocation", "completed"),
("", "completed_empty")]:
def fake(path, body):
self.assertEqual(path, "/api/v1/check")
self.assertEqual(body, {"service_type": "carrier",
"identifier": A})
return completed(A, value)
result = client(fake, [A])
self.assertNotIn("extra", result["data"])
self.assertNotIn("registered", result["data"])
self.assertEqual(classify(result), expected)
def test_error_branches(self):
for code, expected in [(42200, "undetermined"),
(42901, "busy"), (50400, "timeout")]:
with self.subTest(code=code):
result = client(lambda path, body: {
"code": code, "msg": "synthetic error",
"data": None}, [A])
self.assertEqual(classify(result), expected)
def test_multi_order_and_no_result(self):
def fake(path, body):
self.assertEqual(path, "/api/v1/batch-check")
self.assertEqual(body, {"service_type": "carrier",
"identifiers": [B, A]})
return {"code": 0, "msg": "synthetic fixture", "data": {
"service_type": "carrier", "total": 2,
"succeeded": 1, "failed": 1, "results": [
{"identifier": B, "exists": True,
**dict.fromkeys(FIELDS, "")},
{"identifier": A, "exists": False}]}}
data = client(fake, [B, A])["data"]
rows = data["results"]
self.assertEqual(data["service_type"], "carrier")
self.assertEqual([r["identifier"] for r in rows], [B, A])
self.assertEqual(len(rows), data["total"])
outcomes = ["completed" if r["exists"] else "no_result"
for r in rows]
self.assertEqual(outcomes, ["completed", "no_result"])
self.assertNotIn("carrier", rows[1])
def test_guard_rejects_an_escape(self):
with socket.socket() as sock:
with self.assertRaisesRegex(AssertionError, "network forbidden"):
sock.connect(("127.0.0.1", 9))
if __name__ == "__main__":
unittest.main()
Das Beispiel gibt bereits dekodierte Umschläge von seinem Transport zurück. Es testet die Konstruktion der Anfrage und die Handhabung der Ergebnisse, anstatt das HTTP-Parsing zu prüfen. Behalten Sie Fixtures für HTTP-Status, Header und fehlerhaftes JSON auch an Ihrer echten Transportgrenze bei. Insbesondere enthält eine 42901-Antwort Retry-After; ein Response-Header-Fixture sollte überprüfen, ob Ihr Transport dies für den Aufrufer beibehält. Hardcodieren Sie kein neues Wiederholungsintervall in den Ergebnis-Klassifizierer.
Der Socket-Guard testet absichtlich seinen eigenen Fehlerpfad. Er blockiert die Socket-Verbindungsmethode dieses Python-Prozesses; die separate CI-Egress-Einschränkung bleibt für andere Pfade notwendig. Ein versehentlicher kostenpflichtiger Anruf sollte den Job fehlschlagen lassen, anstatt ein unbemerktes zusätzliches Fixture zu werden.
Gestalten Sie Multi-Ergebnis-Assertionen empfindlich für Sortierfehler
Das Multi-Fixture übermittelt B vor A. Das ist wichtig: Ein Test, der alphabetisch sortierte Identifikatoren übermittelt und dieselbe sortierte Reihenfolge erhält, kann einen Adapter übersehen, der Antworten sortiert. Ordnen Sie jeden zurückgegebenen Identifikator seiner Eingabeposition zu und validieren Sie die Gesamtlänge.
Das erste Multi-Element erzeugt ein abgeschlossenes Ergebnis mit leeren Zuweisungswerten. Das zweite hat exists=false und keine Carrier-Felder. Behalten Sie diesen Unterschied bei der Zuordnung zu Anwendungsdatensätzen bei. Wenn Ihr Produktionsadapter fehlgeschlagene Zeilen verwirft, sollte das Fixture fehlschlagen, da spätere Zeilen sonst auf die falschen Kontakte verschoben werden könnten.
Fügen Sie eine Assertion an der tatsächlichen CRM-Mapping-Funktion hinzu: Eine unbestimmte oder fehlgeschlagene Abfrage muss die vorherige Zuweisungsbeobachtung intakt lassen oder sie gemäß Ihrer Richtlinie explizit als veraltet markieren. Sie darf den Datensatz nicht mit „ungültige Nummer“ überschreiben. Derselbe Test sollte die Ableitung des aktuellen Standorts aus der Zuweisungsregion oder -stadt ablehnen.
Ein synchroner Multi-Timeout lässt die gesamte Anfrage ohne Teilergebnisse fehlschlagen. Testen Sie den Timeout-Zweig der gesamten Anfrage separat von einem erfolgreichen Umschlag, der ein nicht erfolgreiches Element enthält. Dies sind unterschiedliche Eingaben für den Consumer.
Halten Sie Offline-Vertrauen von Live-Verifizierung getrennt
Diese Tests legen fest, wie Ihr Code auf den durch die Fixtures repräsentierten Vertrag reagiert. Sie demonstrieren nicht die aktuelle Verfügbarkeit des Anbieters, die Länderakzeptanz, Kontoberechtigungen oder Abrechnungen. Ein fabriziertes Fixture kann auch nicht beweisen, dass ein Live-Anbieter ein bestimmtes Zuweisungsfeld für eine bestimmte Nummer zurückgibt.
Bewahren Sie die Herkunft der Fixtures neben dem Test auf: die CarrierLookup API reference, das Überprüfungsdatum und die Felder, die Ihr Adapter konsumiert. Aktualisieren Sie Fixtures, wenn sich ein dokumentierter Vertrag ändert, und prüfen Sie dann, ob alte Anwendungsdaten einen kompatiblen Lesepfad benötigen.
Führen Sie einen separat autorisierten Live-Smoke-Test durch, wenn eine Integrationsverifizierung dies erfordert, unter Verwendung genehmigter Eingaben und eines eigenen Guthabenbudgets. Lassen Sie das normale CI nicht von diesem Aufruf abhängen. Beginnen Sie damit, einen Live-Client-Import in Ihrer Test-Suite durch einen injizierten Transport zu ersetzen und zu bestätigen, dass ein versuchter Netzwerkausbruch sofort fehlschlägt.
Mehr erfahren
Wählen Sie die Produktinformationen, die zum nächsten Schritt in Ihrem Workflow passen.