Die Programmiersprache Rust hat sich in den letzten Jahren zu einer der spannendsten Technologien für moderne Systementwicklung entwickelt – und das aus gutem Grund. Rust verbindet Sicherheit und Performance mit durchdachten Sprachkonzepten, die Entwickelnde nicht nur produktiver, sondern auch fehlerfreier arbeiten lassen. In diesem Artikel werfen wir einen kompakten Blick auf die Entstehung von Rust, seine wichtigsten Sprachmerkmale wie Typsicherheit und Ownership – und beleuchten, warum Rust auch im Bereich Testing mit starken, integrierten Werkzeugen überzeugt.

Entwickelt wurde die Programmiersprache Rust vom kanadischen Software-Ingenieur Graydon Hoare. Ursprünglich konzentrierte sich seine Arbeit auf Compiler, Profiler, Debugging-Tools und verteilte Systeme. Als Graydon Hoare sein Rust-Projekt seinem Manager bei Mozilla zeigte, erkannte Mozilla den Wert von Rust und erlaubte Hoare, ein Team unter Mozilla Research zu leiten. Am 15. Mai 2015 erschien Rust 1.1.0 als erste stabile Version.

Nach Mozillas Umstrukturierungen wurde im Jahr 2021 die unabhängige Rust Foundation gegründet, zu deren Gründungsmitgliedern große Technologieunternehmen wie Amazon Web Services (AWS), Google, Microsoft, Huawei und Mozilla gehören.

Abb. 1: Ferris the Crab, inoffizielles Maskottchen von Rust, https://rustacean.net/

Zu den Hauptvorteilen von Rust zählen vor allem die Typsicherheit und das Ownership-Konzept. Typsicherheit ist ein wesentlicher Aspekt von Programmiersprachen, der sicherstellt, dass Variablen konsistent gemäß ihren definierten Typen verwendet werden. Rust, das für seinen starken Fokus auf Sicherheit und Performance bekannt ist, hebt die Typsicherheit auf eine neue Ebene.

 

Zusammenfassend lässt sich sagen, dass Rusts Ansatz zur Typsicherheit einen erheblichen Vorteil für Entwickler:innen darstellt. Durch das Abfangen von Fehlern zur Kompilierungszeit und die Bereitstellung leistungsfähiger Funktionen hilft Rust bei der Erstellung robuster und zuverlässiger Software. Dieser Fokus auf Sicherheit und Leistung macht Rust zu einer überzeugenden Wahl für die Systemprogrammierung und darüber hinaus.

Ein zweiter wichtiger Vorteil der Programmiersprache Rust ist das Konzept von Ownership. Ownership ist ein Regelwerk, das festlegt, wie ein Rust-Programm den Speicher verwaltet.

Rust weist Merkmale der objektorientierten Programmierung auf und ist von Skriptsprachen und funktionaler Programmierung beeinflusst.

 

Der Einfluss funktionaler Programmiersprachen auf Rust zeigt sich unter anderem in einer gewissen Ähnlichkeit des Rust-Paketmanagers cargo mit node.js. Die Struktur der Rust-Konfigurationsdatei cargo.toml ähnelt der Struktur von package.json. Iteratoren und Closures sind weitere Merkmale der funktionalen Programmierung in Rust.

Borrowing, References und Lifetimes sind drei Hauptkonzepte in Rust, die ebenfalls zu den Vorteilen dieser Programmiersprache gezählt werden können.

Zum Abschluss dieser kurzen Einführung in die Programmiersprache Rust möchte ich noch den match-Ausdruck erwähnen.

Der match-Ausdruck in Rust ist ein mächtiger Kontrollflussoperator, der es ermöglicht, einen Wert mit einer Reihe von Mustern zu vergleichen und Code auszuführen, je nachdem, welches Muster übereinstimmt. Er ähnelt der switch-Anweisung in anderen Sprachen, ist aber mächtiger und flexibler.

Rust verfügt über eine integrierte Testinfrastruktur: Unit-Tests werden mit dem Attribut #[test] direkt im Quellcode definiert, und mit dem Befehl cargo test können alle Tests automatisch ausgeführt werden.

Unit-Tests können ohne Installation zusätzlicher Software entwickelt werden. Unit-Tests in Rust haben folgende Struktur:

#[test]
fn it_adds_four_and_five() {
    let result = my_add(4, 5);
    assert_eq!(result, 9);
}

Jeder Unit-Test ist eine Funktion, die einen Aufruf der zu testenden Funktion und einen Assertion-Ausdruck enthält. Unit-Tests in Rust sollten so nah wie möglich am zu testenden Code platziert werden. Es ist auch nicht unüblich, sogenannte Inline-Tests zu entwickeln, die direkt in der Datei mit dem Code stehen, der getestet wird. Sollten die Assertions aus dem vorhandenen Test-Crate nicht ausreichen, kann das GoogleTest-Framework installiert werden, zum Beispiel, durch Hinzufügen einer GoogleTest Dependency in der Konfigurationsdatei Cargo.toml.

Diese Variante eignet sich vor allem für QA-Ingenieure, die das GoogleTest-Framework für C++-Code-Tests verwendet haben.

Negative Tests, d.h. Tests, von denen erwartet wird, dass sie fehlschlagen, werden mit dem Flag #[should_panic] gekennzeichnet:

#[test]
#[should_panic]
fn it_check_function_failed() {
     let result = addition(4, 1);
     assert_eq!(result, 500);
}

Integrationstests sind in Rust wie folgt definiert: Unit Tests testen jeweils ein Modul in Isolation: Sie sind klein und können privaten Code testen. Integrationstests sind extern zu dem zu testenden Crate und benutzen nur dessen öffentliche Schnittstelle, wie jeder andere Code auch. Ihr Zweck ist es zu testen, ob alle der Bibliothek korrekt zusammenarbeiten. Zu diesem Zweck werden die Integrationstests in einer speziellen Ordnerstruktur in separaten Dateien im Verzeichnis tests erstellt.

Der Integrationstestordner befindet sich also auf der gleichen Ebene wie der zu testende Quellcode. Die Tests werden durch den Aufruf von cargo test ausgeführt.

Um eine API zu testen, kann z.B. das Modul Wire-Mock verwendet werden. Das Modul kann auch installiert werden, indem eine neue Abhängigkeit zur Konfigurationsdatei Cargo.toml hinzugefügt wird.

Besondere Aufmerksamkeit sollte den Snapshot-Tests gewidmet werden. Snapshot-Tests unterscheiden sich stark von Unit- und Funktionstests. Das Ziel dieser Tests ist es, die aufgezeichneten Eigenschaften eines Systems mit den aktuellen Eigenschaften zu vergleichen. Um Snapshot-Tests zu entwickeln, wird das Insta-Crate installiert. Der Hauptvorteil von Snapshot-Tests ist ihre schnelle Entwicklung. Sobald das Insta-Crate installiert ist, können Snapshot-Tests ohne weitere Konfiguration entwickelt werden.

Im Testprozess ist es unter anderem wichtig, die Testabdeckung zu bestimmen. Zu diesem Zweck wird das Crate tarpaulin in der Rust-Testumgebung installiert.

In der Programmiersprache Rust gibt es für jede klassische Testebene ein dediziertes Testwerkzeug, so dass ein solider und effizienter Testprozess aufgebaut werden kann.

Fazit

Aus dieser Analyse lässt sich ableiten, dass die Programmiersprache Rust neben den verbesserten Konzepten wie Ownership und Typsicherheit auch im Bereich des Testens viele nützliche Built-In Features mitbringt. Rust bietet damit Entwickelnden ein starkes Fundament – von der Systemprogrammierung bis hin zu Webanwendungen.

Über die Autorin:

Maria Shalnova-Weinzierl studierte Informatik und Computerlinguistik an der LMU München. Sie ist eine erfahrene Ingenieurin für Testautomatisierung. Sie hat in der Testautomatisierung in verschiedenen Branchen gearbeitet: Gerätebau, Datensicherheit, Medizin und Automotive. Maria arbeitet bei der Carl Zeiss Digital Innovation GmbH als Senior QA Engineer.