Azure Service Bus Client Library for JavaScript - Version 7.10.0

Azure Service Bus ist ein äußerst zuverlässiger Cloudmessagingdienst von Microsoft.

Verwenden Sie die Clientbibliothek @azure/service-bus in Ihrer Anwendung, um

  • Senden von Nachrichten an eine Azure Service Bus-Warteschlange oder ein Azure Service Bus-Thema
  • Empfangen von Nachrichten aus einer Azure Service Bus-Warteschlange oder einem Azure Service Bus-Abonnement
  • Erstellen/Abrufen/Löschen/Aktualisieren/Auflisten von Warteschlangen/Themen/Abonnements/Regeln in einem Azure Service Bus-Namespace.

Ressourcen für @azure/service-bus Version 7:

Wichtige Links:

HINWEIS: Wenn Sie Version 1.1.10 oder niedriger verwenden und auf die neueste Version dieses Pakets migrieren möchten, lesen Sie unseren Migrationsleitfaden zum Wechseln von Service Bus V1 zu Service Bus V7

Erste Schritte

Installiere das Paket

Installieren Sie die neueste Version für die Azure Service Bus-Clientbibliothek mit npm.

npm install @azure/service-bus

Derzeit unterstützte Umgebungen

Prerequisites

Konfigurieren von TypeScript

TypeScript-Benutzer müssen Knotentypdefinitionen installiert haben:

npm install @types/node

Sie müssen auch compilerOptions.allowSyntheticDefaultImports in Ihrem tsconfig.jsonaktivieren. Wenn Sie compilerOptions.esModuleInteropaktiviert haben, ist allowSyntheticDefaultImports standardmäßig aktiviert. Weitere Informationen finden Sie im Compileroptionenhandbuch TypeScript.

JavaScript-Bündel

Um diese Clientbibliothek im Browser zu verwenden, müssen Sie zuerst einen Bundler verwenden. Ausführliche Informationen dazu finden Sie in unserer Bündelungsdokumentation.

Zusätzlich zu den dort beschriebenen Komponenten benötigt diese Bibliothek zusätzliche Polyfills für die folgenden integrierten NodeJS-Kernmodule, um in den Browsern ordnungsgemäß zu funktionieren:

  • buffer
  • os
  • path
  • process

Bündelung mit Webpack

Wenn Sie Webpack v5 verwenden, können Sie die folgenden Entwicklungsabhängigkeiten installieren.

  • npm install --save-dev os-browserify path-browserify

fügen Sie dann Folgendes zu Ihrer webpack.config.js

 const path = require("path");
+const webpack = require("webpack");

 module.exports = {
   entry: "./src/index.ts",
@@ -12,8 +13,21 @@ module.exports = {
       },
     ],
   },
+  plugins: [
+    new webpack.ProvidePlugin({
+      process: "process/browser",
+    }),
+    new webpack.ProvidePlugin({
+      Buffer: ["buffer", "Buffer"],
+    }),
+  ],
   resolve: {
     extensions: [".ts", ".js"],
+    fallback: {
+      buffer: require.resolve("buffer/"),
+      os: require.resolve("os-browserify"),
+      path: require.resolve("path-browserify"),
+    },
   },

Bündelung mit Rollup

Wenn Sie rollup bundler verwenden, installieren Sie die folgenden Entwicklungsabhängigkeiten:

  • npm install --save-dev @rollup/plugin-commonjs @rollup/plugin-inject @rollup/plugin-node-resolve

Schließen Sie dann Folgendes in Ihre rollup.config.js ein.

+import nodeResolve from "@rollup/plugin-node-resolve";
+import cjs from "@rollup/plugin-commonjs";
+import shim from "rollup-plugin-shim";
+import inject from "@rollup/plugin-inject";

export default {
  // other configs
  plugins: [
+    shim({
+      fs: `export default {}`,
+      net: `export default {}`,
+      tls: `export default {}`,
+      path: `export default {}`,
+      dns: `export function resolve() { }`,
+    }),
+    nodeResolve({
+      mainFields: ["module", "browser"],
+      preferBuiltins: false,
+    }),
+    cjs(),
+    inject({
+      modules: {
+        Buffer: ["buffer", "Buffer"],
+        process: "process",
+      },
+      exclude: ["./**/package.json"],
+    }),
  ]
};

Weitere Informationen zur Verwendung von Polyfills finden Sie in der Dokumentation Ihres bevorzugten Bundlers.

React Native-Unterstützung

Ähnlich wie Browser unterstützt React Native einige JavaScript-APIs, die von dieser SDK-Bibliothek verwendet werden, nicht, sodass Sie Polyfills für sie bereitstellen müssen. Weitere Informationen finden Sie im Messaging React Native-Beispiel mit Expo.

Authentifizieren des Clients

Die Interaktion mit Service Bus beginnt mit einer Instanz der ServiceBusClient-Klasse . Sie können sich bei Service Bus mithilfe einer Verbindungszeichenfolge oder mit Azure Active Directory-Anmeldeinformationen authentifizieren.

Verwenden einer Verbindungszeichenfolge

Diese Methode übernimmt die Verbindungszeichenfolge für Ihre Service Bus-Instanz. Sie können die Verbindungszeichenfolge aus dem Azure-Portal abrufen.

import { ServiceBusClient } from "@azure/service-bus";

const serviceBusClient = new ServiceBusClient("<connectionString>");

Weitere Informationen zu diesem Konstruktor finden Sie in der API-Dokumentation.

Verwenden von Azure Active Directory-Anmeldeinformationen

Bei der Authentifizierung mit Azure Active Directory wird die Azure Identity-Bibliothek verwendet.

Im folgenden Beispiel wird DefaultAzureCredential verwendet, einer von vielen verfügbaren Anmeldeinformationsanbietern aus der @azure/identity Bibliothek.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

HINWEIS: Wenn Sie Ihre eigene Implementierung der TokenCredential Schnittstelle für AAD verwenden, legen Sie die "Bereiche" für service-bus auf Folgendes fest, um das entsprechende Token abzurufen:

["https://servicebus.azure.net//user_impersonation"];

Weitere Informationen zu diesem Konstruktor finden Sie in der API-Dokumentation

Schlüsselkonzepte

Nachdem Sie eine initialisiert ServiceBusClienthaben, können Sie mit den folgenden Ressourcen in einem Service Bus-Namespace interagieren:

  • Warteschlangen: Ermöglicht das Senden und Empfangen von Nachrichten. Wird häufig für die Punkt-zu-Punkt-Kommunikation verwendet.
  • Themen: Im Gegensatz zu Warteschlangen eignen sich Themen besser für Publish/Subscribe-Szenarien. Ein Thema kann gesendet werden, erfordert jedoch ein Abonnement, von dem es mehrere parallel geben kann, um es zu nutzen.
  • Abonnements: Der Mechanismus für die Nutzung eines Themas. Jedes Abonnement ist unabhängig und erhält eine Kopie jeder Nachricht, die an das Thema gesendet wird. Regeln und Filter können verwendet werden, um anzupassen, welche Nachrichten von einem bestimmten Abonnement empfangen werden.

Weitere Informationen zu diesen Ressourcen finden Sie unter Was ist Azure Service Bus?.

Um mit diesen Ressourcen interagieren zu können, sollte man mit den folgenden SDK-Konzepten vertraut sein:

Bitte beachten Sie, dass die Warteschlangen, Themen und Abonnements vor der Verwendung dieser Bibliothek erstellt werden sollten.

Examples

In den folgenden Abschnitten finden Sie Codeausschnitte, die einige der allgemeinen Aufgaben bei der Verwendung von Azure Service Bus abdecken

Nachrichten senden

Nachdem Sie eine Instanz einer ServiceBusClient Klasse erstellt haben, können Sie eine ServiceBusSender mit der createSender-Methode abrufen, die Sie zum Senden von Nachrichten verwenden können.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const sender = serviceBusClient.createSender("my-queue");

const messages = [
  { body: "Albert Einstein" },
  { body: "Werner Heisenberg" },
  { body: "Marie Curie" },
  { body: "Steven Hawking" },
  { body: "Isaac Newton" },
  { body: "Niels Bohr" },
  { body: "Michael Faraday" },
  { body: "Galileo Galilei" },
  { body: "Johannes Kepler" },
  { body: "Nikolaus Kopernikus" },
];

// sending a single message
await sender.sendMessages(messages[0]);

// sending multiple messages in a single call
// this will fail if the messages cannot fit in a batch
await sender.sendMessages(messages);

// Sends multiple messages using one or more ServiceBusMessageBatch objects as required
let batch = await sender.createMessageBatch();

for (let i = 0; i < messages.length; i++) {
  const message = messages[i];
  if (!batch.tryAddMessage(message)) {
    // Send the current batch as it is full and create a new one
    await sender.sendMessages(batch);
    batch = await sender.createMessageBatch();

    if (!batch.tryAddMessage(messages[i])) {
      throw new Error("Message too big to fit in a batch");
    }
  }
}
// Send the batch
await sender.sendMessages(batch);

Empfangen von Nachrichten

Nachdem Sie eine Instanz einer ServiceBusClient Klasse erstellt haben, können Sie eine ServiceBusReceiver mit der createReceiver-Methode abrufen.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const receiver = serviceBusClient.createReceiver("my-queue");

Es stehen zwei receiveModes zur Verfügung.

  • "peekLock" – Im peekLock-Modus hat der Empfänger eine Sperre für die Nachricht, die in der Warteschlange angegeben ist.
  • "receiveAndDelete": Im receiveAndDelete-Modus werden Nachrichten beim Empfang aus Service Bus gelöscht.

Wenn der receiveMode in den Optionen nicht angegeben ist, wird standardmäßig der "peekLock"-Modus verwendet. Sie können die empfangenen Nachrichten auch im "peekLock"-Modus abrechnen.

Sie können diesen Empfänger auf eine von 3 Arten verwenden, um Nachrichten zu empfangen:

Abrufen eines Arrays von Nachrichten

Verwenden Sie die receiveMessages-Funktion , die eine Zusage zurückgibt, die in ein Array von Nachrichten aufgelöst wird.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const receiver = serviceBusClient.createReceiver("my-queue");

const myMessages = await receiver.receiveMessages(10);

Abonnieren mit einem Nachrichtenhandler

Verwenden Sie die subscribe-Methode , um Nachrichtenhandler einzurichten und sie so lange wie nötig auszuführen.

Wenn Sie fertig sind, rufen Sie an receiver.close() , um keine Nachrichten mehr zu erhalten.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const receiver = serviceBusClient.createReceiver("my-queue");

const myMessageHandler = async (message) => {
  // your code here
  console.log(`message.body: ${message.body}`);
};
const myErrorHandler = async (args) => {
  console.log(
    `Error occurred with ${args.entityPath} within ${args.fullyQualifiedNamespace}: `,
    args.error,
  );
};
receiver.subscribe({
  processMessage: myMessageHandler,
  processError: myErrorHandler,
});

Verwenden des asynchronen Iterators

Verwenden Sie getMessageIterator , um einen asynchronen Iterator für Nachrichten abzurufen

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const receiver = serviceBusClient.createReceiver("my-queue");

for await (const message of receiver.getMessageIterator()) {
  // your code here
}

Begleichen einer Nachricht

Sobald Sie eine Nachricht erhalten haben, completeMessage() können Sie , abandonMessage()oder deferMessage() auf den Empfänger anrufendeadLetterMessage(), je nachdem, wie Sie die Nachricht abrechnen möchten.

Um mehr zu erfahren, lesen Sie bitte Empfangene Nachrichten abrechnen

Warteschlangen für unzustellbare Nachrichten

Bei der Warteschlange für unzustellbare Nachrichten handelt es sich um eine Unterwarteschlange. Jede Warteschlange oder jedes Abonnement verfügt über eine eigene Warteschlange für unzustellbare Nachrichten. In Warteschlangen für unzustellbare Nachrichten werden Nachrichten gespeichert, die explizit als unzustellbare Nachrichten (via receiver.deadLetterMessage()) gekennzeichnet wurden, oder Nachrichten, die ihre maximale Übermittlungsanzahl überschritten haben.

Das Erstellen eines Empfängers für eine Unterwarteschlange für unzustellbare Nachrichten ähnelt dem Erstellen eines Empfängers für ein Abonnement oder eine Warteschlange:

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

// To receive from a queue's dead letter sub-queue
const deadLetterReceiverForQueue = serviceBusClient.createReceiver("queue", {
  subQueueType: "deadLetter",
});

// To receive from a subscription's dead letter sub-queue
const deadLetterReceiverForSubscription = serviceBusClient.createReceiver("topic", "subscription", {
  subQueueType: "deadLetter",
});

// Dead letter receivers work like any other receiver connected to a queue
// ex:
const messages = await deadLetterReceiverForQueue.receiveMessages(5);

for (const message of messages) {
  console.log(`Dead lettered message: ${message.body}`);
}

Vollständige Beispiele, die Warteschlangen für unzustellbare Nachrichten ausführlicher veranschaulichen:

Senden von Nachrichten mithilfe von Sitzungen

Für die Verwendung von Sitzungen müssen Sie eine sitzungsaktivierte Warteschlange oder ein Abonnement erstellen. Weitere Informationen zur Konfiguration dieser Funktion im Portal finden Sie hier.

Um Nachrichten an eine Sitzung zu senden, verwenden Sie die ServiceBusClient , um einen Absender mit createSender zu erstellen.

Legen Sie beim Senden der Nachricht die sessionId Eigenschaft in der Nachricht fest, um sicherzustellen, dass die Nachricht in der richtigen Sitzung landet.

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

const sender = serviceBusClient.createSender("my-session-queue");
await sender.sendMessages({
  body: "my-message-body",
  sessionId: "my-session",
});

Weitere Informationen zur Funktionsweise von Sitzungen finden Sie hier.

Empfangen von Nachrichten aus Sitzungen

Für die Verwendung von Sitzungen müssen Sie eine sitzungsaktivierte Warteschlange oder ein Abonnement erstellen. Weitere Informationen zur Konfiguration dieser Funktion im Portal finden Sie hier.

Im Gegensatz zu nicht sitzungsaktivierten Warteschlangen oder Abonnements kann immer nur ein einzelner Empfänger aus einer Sitzung lesen. Dies wird durch Sperren einer Sitzung erzwungen, die von Service Bus verarbeitet wird. Konzeptionell ähnelt dies der Funktionsweise der Nachrichtensperre bei Verwendung peekLock des Modus: Wenn eine Nachricht (oder Sitzung) gesperrt ist, hat der Empfänger exklusiven Zugriff darauf.

Um eine Sitzung zu öffnen und zu sperren, verwenden Sie eine Instanz von ServiceBusClient , um einen SessionReceiver zu erstellen.

Es gibt zwei Möglichkeiten, auszuwählen, welche Sitzung geöffnet werden soll:

  1. Geben Sie einen Wert an sessionId, der eine benannte Sitzung sperrt.

    const receiver = await serviceBusClient.acceptSession("my-session-queue", "my-session");
    
  2. Geben Sie keine Sitzungs-ID an. In diesem Fall sucht Service Bus die nächste verfügbare Sitzung, die noch nicht gesperrt ist.

    const receiver = await serviceBusClient.acceptNextSession("my-session-queue");
    

    Den Namen der Sitzung finden Sie über die sessionId Eigenschaft auf der SessionReceiver. Wenn der receiveMode in den Optionen nicht angegeben ist, wird standardmäßig der "peekLock"-Modus verwendet. Sie können die empfangenen Nachrichten auch im "peekLock"-Modus abrechnen.

Sobald der Empfänger erstellt ist, können Sie zwischen 3 Möglichkeiten wählen, um Nachrichten zu empfangen:

Weitere Informationen zur Funktionsweise von Sitzungen finden Sie hier.

Nachrichtensitzungen auflisten

Um herauszufinden, welche Sitzungen aktive Nachrichten oder den Sitzungsstatus in einer Warteschlange oder einem Abonnement haben, verwenden listMessageSessions()Sie :

import { DefaultAzureCredential } from "@azure/identity";
import { ServiceBusClient } from "@azure/service-bus";

const fullyQualifiedNamespace = "<name-of-service-bus-namespace>.servicebus.windows.net";
const credential = new DefaultAzureCredential();
const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);

// List all sessions with active messages or session state in a queue
for await (const sessionId of serviceBusClient.listMessageSessions("my-session-queue")) {
  console.log("Session ID:", sessionId);
}

// List sessions in a subscription
for await (const sessionId of serviceBusClient.listMessageSessions("my-topic", "my-subscription")) {
  console.log("Session ID:", sessionId);
}

// List only sessions whose stored session state was set or updated in the last seven days
const sessionStateUpdatedAfter = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
for await (const sessionId of serviceBusClient.listMessageSessions("my-session-queue", {
  sessionStateUpdatedAfter,
})) {
  console.log("Recently updated session ID:", sessionId);
}

Verwalten von Ressourcen eines Service Bus-Namespaces

ServiceBusAdministrationClient Ermöglicht die Verwaltung eines Namespace mit CRUD-Operationen für die Entitäten (Warteschlangen, Themen und Abonnements) und für die Regeln eines Abonnements.

  • Unterstützt die Authentifizierung mit einer Service Bus-Verbindungszeichenfolge sowie mit den AAD-Anmeldeinformationen von @azure/identity .ServiceBusClient

Hinweis: Service Bus unterstützt das Festlegen von CORS-Regeln für Namespaces noch nicht und funktioniert daher ServiceBusAdministrationClient im Browser nicht, ohne die Websicherheit zu deaktivieren. Weitere Informationen finden Sie hier.

import { ServiceBusAdministrationClient } from "@azure/service-bus";

const queueName = "my-session-queue";

// Get the connection string from the portal
// OR
// use the token credential overload, provide the host name of your Service Bus instance and the AAD credentials from the @azure/identity library
const serviceBusAdministrationClient = new ServiceBusAdministrationClient("<connectionString>");

// Similarly, you can create topics and subscriptions as well.
const createQueueResponse = await serviceBusAdministrationClient.createQueue(queueName);
console.log("Created queue with name - ", createQueueResponse.name);

const queueRuntimeProperties =
  await serviceBusAdministrationClient.getQueueRuntimeProperties(queueName);
console.log(`Number of messages in the queue = ${queueRuntimeProperties.totalMessageCount}`);

// Topic runtime properties additionally report the total number of SQL and correlation filters
// across all of the topic's subscriptions. These counts are served by the 2024-05 service API
// version and later; on an older version they are `undefined`.
const topicName = "my-topic";
const subscriptionName = "my-subscription";
await serviceBusAdministrationClient.createTopic(topicName);
// A new subscription carries a default rule with a SQL TrueFilter. Adding a correlation rule
// gives the topic one of each, so the counts below aggregate across the subscription's rules.
await serviceBusAdministrationClient.createSubscription(topicName, subscriptionName);
await serviceBusAdministrationClient.createRule(
  topicName,
  subscriptionName,
  "my-correlation-rule",
  { correlationId: "my-correlation-id" },
);
const topicRuntimeProperties =
  await serviceBusAdministrationClient.getTopicRuntimeProperties(topicName);
console.log(`SQL filter count = ${topicRuntimeProperties.sqlFilterCount}`);
console.log(`Correlation filter count = ${topicRuntimeProperties.correlationFilterCount}`);

await serviceBusAdministrationClient.deleteTopic(topicName);
await serviceBusAdministrationClient.deleteQueue(queueName);

Troubleshooting

Hier sind einige erste Schritte, um mit der Diagnose von Problemen zu beginnen. Weitere Informationen finden Sie im Service Bus-Leitfaden zur Problembehandlung.

AMQP-Abhängigkeiten

Die Service Bus-Bibliothek ist von der rhea-promise-Bibliothek abhängig, um Verbindungen zu verwalten, Nachrichten über das AMQP-Protokoll zu senden und zu empfangen.

Logging

Sie können die folgende Umgebungsvariable festlegen, um die Debugprotokolle abzurufen, wenn Sie diese Bibliothek verwenden.

  • Abrufen von Debugprotokollen aus dem Service Bus SDK
export DEBUG=azure*
  • Abrufen von Debugprotokollen aus dem Service Bus SDK und der Bibliothek auf Protokollebene.
export DEBUG=azure*,rhea*
  • Wenn Sie nicht daran interessiert sind, die Nachrichtentransformation anzuzeigen (die viel Konsolen-/Festplattenspeicher verbraucht), können Sie die DEBUG Umgebungsvariable wie folgt festlegen:
export DEBUG=azure*,rhea*,-rhea:raw,-rhea:message,-azure:core-amqp:datatransformer
  • Wenn Sie nur an Fehlern interessiert sind, können Sie die DEBUG Umgebungsvariable wie folgt festlegen:
export DEBUG=azure:service-bus:error,azure:core-amqp:error,rhea-promise:error,rhea:events,rhea:frames,rhea:io,rhea:flow

Anmelden bei einer Datei

  1. Legen Sie die DEBUG Umgebungsvariable wie oben gezeigt fest
  2. Führen Sie das Testskript wie folgt aus:
  • Protokollierungsanweisungen aus Ihrem Testskript gehen zu out.log und Protokollierungsanweisungen aus dem SDK wechseln zu debug.log.
    node your-test-script.js > out.log 2>debug.log
    
  • Protokollierungsanweisungen aus Ihrem Testskript und das SDK gehen zu derselben Datei out.log, indem Sie stderr zu stdout (&1) umleiten und dann stdout an eine Datei umleiten:
    node your-test-script.js >out.log 2>&1
    
  • Protokollierungsanweisungen aus Ihrem Testskript und das SDK wechseln zu derselben Datei out.log.
      node your-test-script.js &> out.log
    

Nächste Schritte

Werfen Sie einen Blick auf das Beispielverzeichnis , um detaillierte Beispiele für die Verwendung dieser Bibliothek zum Senden und Empfangen von Nachrichten an/von Service Bus-Warteschlangen, -Themen und -Abonnements zu erhalten.

Contributing

Wenn Sie zu dieser Bibliothek beitragen möchten, lesen Sie bitte den beitragenden Leitfaden, um mehr darüber zu erfahren, wie man den Code erstellt und testet.