Vue Test Utils: shallowMount vs. mount im Detail
AI generated
{ }
Vue · Testing · Vue Test Utils
shallowMount vs. mount
Wann Kindkomponenten gestubbt werden sollten und wann nicht

shallowMount ersetzt jede Kindkomponente durch einen Stub und rendert nur die getestete Komponente selbst, mount baut den kompletten Komponentenbaum auf. Beide Funktionen aus Vue Test Utils lösen unterschiedliche Testprobleme, und wer sie verwechselt, bekommt entweder Tests, die zu viel gleichzeitig prüfen, oder Tests, die an internen Details von Kindkomponenten zerbrechen.

13 Min. Lesezeit shallowMount vs. mount Vue Test Utils

1. Was shallowMount und mount unterschiedlich machen

Beide Funktionen aus @vue/test-utils rendern eine Vue-Komponente in einer isolierten Testumgebung und geben einen Wrapper zurück, über den sich der gerenderte Baum inspizieren, Events auslösen und Props ändern lassen. Der entscheidende Unterschied liegt darin, was mit Kindkomponenten passiert. mount() rendert jede Kindkomponente vollständig, inklusive ihrer eigenen setup()-Logik, Lifecycle-Hooks und aller weiteren Verschachtelungen. shallowMount() dagegen ersetzt jede Kindkomponente automatisch durch einen einfachen Stub, der nur den Komponentennamen als Platzhalter-Tag rendert, ohne die eigentliche Implementierung auszuführen.

Das klingt nach einer rein technischen Feinheit, hat aber direkte Konsequenzen für das, was ein Test eigentlich aussagt. Ein Test mit mount() prüft implizit auch das Verhalten aller Kindkomponenten mit, weil sie tatsächlich ausgeführt werden. Ein Fehler in einer tief verschachtelten Kindkomponente kann dadurch einen Test zum Scheitern bringen, der eigentlich nur die Elternkomponente betrifft. shallowMount() kappt diese Kette bewusst und macht den Test unempfindlich gegenüber allem, was innerhalb der Kindkomponenten passiert, solange nur deren öffentliche Schnittstelle über Props und Events stimmt.


import { describe, it, expect } from 'vitest'
import { shallowMount, mount } from '@vue/test-utils'
import ProductCard from '@/components/ProductCard.vue'

describe('ProductCard', () => {
  it('rendert Kindkomponenten nur als Stub (isoliert)', () => {
    const wrapper = shallowMount(ProductCard, {
      props: { name: 'Teekanne', price: 29.9 },
    })

    // PriceTag wird nicht wirklich ausgeführt, nur als Stub-Tag gerendert
    expect(wrapper.findComponent({ name: 'PriceTag' }).exists()).toBe(true)
    expect(wrapper.text()).toContain('Teekanne')
  })

  it('rendert den vollen Baum inkl. PriceTag-Logik', () => {
    const wrapper = mount(ProductCard, {
      props: { name: 'Teekanne', price: 29.9 },
    })

    // Hier wird die tatsächliche Formatierung aus PriceTag geprüft
    expect(wrapper.text()).toContain('29,90 €')
  })
})

2. Wann sich shallowMount anbietet

shallowMount passt am besten zu klassischen Unit-Tests, bei denen genau eine Komponente im Fokus steht und ihre Kindkomponenten austauschbare Implementierungsdetails sind. Typische Kandidaten sind Container- oder Layout-Komponenten, die viele Kindkomponenten orchestrieren, aber selbst wenig eigene Logik enthalten außer Props durchzureichen und auf Events zu reagieren. Wird hier mit mount getestet, hängt der Test plötzlich von der korrekten Funktion aller Kinder ab, was Fehlerursachen verschleiert, sobald ein Test rot wird.

Ein weiterer Vorteil zeigt sich bei tiefen Komponentenbäumen: Jede zusätzliche Ebene, die mount tatsächlich rendert, kostet Zeit, insbesondere wenn Kindkomponenten selbst wieder asynchrone Daten laden, Timer starten oder aufwendige Berechnungen im setup() durchführen. Bei einer Testsuite mit mehreren hundert Komponententests summiert sich dieser Unterschied schnell zu spürbar längeren CI-Laufzeiten. shallowMount hält die Ausführungszeit pro Test konstant niedrig, weil immer nur eine Komponentenebene tatsächlich läuft, unabhängig davon, wie tief der reale Baum in Produktion ist.

3. Wann mount die richtige Wahl ist

mount ist unverzichtbar, sobald ein Test das tatsächliche Zusammenspiel mehrerer Komponenten prüfen soll, etwa ob ein Klick in einer Kindkomponente korrekt bis zur Elternkomponente durchgereicht wird und dort eine sichtbare Änderung auslöst. Integrationsartige Tests, die einen kompletten Formular-Flow über mehrere Feldkomponenten hinweg abdecken, ergeben mit gestubbten Kindern wenig Sinn, weil genau die Interaktion zwischen den Teilen der eigentliche Testgegenstand ist.

Auch bei kleinen, rein präsentationsorientierten Komponenten ohne relevante Kindkomponenten macht mount oft den einfacheren Eindruck, weil kein zusätzlicher Gedanke an Stubbing-Strategien nötig ist. Als Faustregel hat sich bewährt: Je näher ein Test an einem echten End-to-End-Szenario innerhalb der Komponentenwelt liegt, desto eher lohnt sich mount. Je isolierter eine einzelne Verantwortlichkeit geprüft werden soll, desto eher gewinnt shallowMount.

4. Fallstricke bei Slots

Ein häufiger Stolperstein bei shallowMount betrifft Slots. Wird eine Kindkomponente gestubbt, rendert Vue Test Utils standardmäßig keinen Slot-Inhalt, den diese Kindkomponente eigentlich an ihrer Stelle einfügen würde, weil der Stub die interne <slot />-Definition nicht kennt. Ein Test, der prüft, ob bestimmter Text über einen Default-Slot in einer Kindkomponente sichtbar wird, schlägt mit shallowMount unter Umständen fehl, obwohl die Komponente in Produktion korrekt funktioniert.

Die Option shallow: false für einzelne Kindkomponenten oder ein gezieltes stubs-Objekt mit { template: '<slot />' } für die betroffene Kindkomponente löst dieses Problem gezielt, ohne gleich auf vollständiges mount umzusteigen. Wichtig ist, sich bewusst zu machen, dass Slot-Verhalten kein Implementierungsdetail einer Kindkomponente ist, sondern Teil ihres öffentlichen Vertrags, und deshalb in Tests, die genau das prüfen wollen, entsprechend konfiguriert werden muss.

5. Fallstricke bei Provide/Inject

Provide/Inject-Ketten sorgen bei shallowMount für ein zweites, subtileres Problem. Da Kindkomponenten gar nicht wirklich ausgeführt werden, wird auch nicht geprüft, ob ein injizierter Wert dort korrekt ankommt und verarbeitet wird. Ein Fehler in der Injection-Kette, etwa ein falscher Injection-Key oder ein fehlender Default-Wert, bleibt mit shallowMount unsichtbar, weil der Stub den inject()-Aufruf der echten Kindkomponente nie erreicht.

Wer Provide/Inject als zentralen Kommunikationsweg zwischen Eltern- und Kindkomponenten nutzt, etwa für ein Formular-Kontext-Objekt, sollte diesen Pfad gezielt mit mount oder zumindest einem gezielt nicht gestubbten Kind testen. Eine sinnvolle Strategie ist, den reinen Aufbau und die Props-Weitergabe mit shallowMount abzudecken und die tatsächliche Provide/Inject-Kommunikation in einem separaten, kleineren Integrationstest mit mount zu verifizieren, statt beides in einem einzigen, überladenen Test zu vermischen.

6. Praktisches Beispiel: beide Ansätze im selben Testfile

In der Praxis schließen sich shallowMount und mount nicht aus, sondern ergänzen sich innerhalb derselben Testdatei je nach Testfall. Für eine Formularkomponente mit mehreren Feld-Unterkomponenten lässt sich mit shallowMount schnell prüfen, ob alle erwarteten Felder überhaupt gerendert werden und ob Props korrekt durchgereicht werden. Für den eigentlichen Absende-Flow, bei dem ein Klick in einem Feld letztlich den Submit-Handler der Elternkomponente mit den richtigen Werten aufrufen soll, ist mount die passendere Wahl, weil hier die reale Interaktion zwischen den Komponenten getestet wird.

Diese Kombination hält die Testsuite insgesamt schnell, weil die Mehrzahl der Tests, die reine Struktur- und Props-Fragen klären, mit dem günstigeren shallowMount auskommt, während die wenigen wirklich interaktionskritischen Tests bewusst den teureren, aber aussagekräftigeren mount-Weg gehen. Wer diese Unterscheidung von Anfang an bewusst trifft, vermeidet sowohl overengineerte Stub-Konfigurationen als auch unnötig langsame Testsuiten.

7. Feingranulare Kontrolle über Stubs

Vue Test Utils erlaubt es, das Alles-oder-nichts-Verhalten von shallowMount gezielt zu durchbrechen. Über die Option global.stubs lässt sich für einzelne Kindkomponenten explizit festlegen, ob sie gestubbt werden sollen oder nicht, etwa stubs: { PriceTag: false }, um genau diese eine Komponente trotz shallowMount vollständig zu rendern. Umgekehrt lässt sich bei mount mit stubs: { HeavyChart: true } gezielt eine einzelne, besonders teure Kindkomponente ausklammern, ohne gleich auf shallowMount für die gesamte Testdatei umzusteigen.

Diese Feinsteuerung ist besonders bei Komponenten wertvoll, die eine einzelne schwergewichtige Abhängigkeit einbinden, etwa eine Chart-Bibliothek oder eine Kartenkomponente, deren Rendering in einer Testumgebung ohnehin wenig Aussagekraft hat und nur Zeit kostet. Statt die gesamte Teststrategie an dieser einen Komponente auszurichten, lässt sich gezielt nur sie stubben, während der Rest des Baums so realistisch wie nötig bleibt.

8. Performance bei tiefen Komponentenbäumen

Bei Design-System-Komponenten, die selbst wieder aus mehreren Ebenen von Unterkomponenten bestehen, etwa ein Datepicker, der intern Kalendergitter, Monatsnavigation und Eingabefeld als eigene Komponenten zusammensetzt, macht sich der Unterschied zwischen shallowMount und mount besonders deutlich bemerkbar. Ein mount-Test auf oberster Ebene rendert in einem solchen Fall unter Umständen dutzende Kindkomponenten mit, obwohl der Test eigentlich nur eine einzelne Prop-Weitergabe prüfen wollte.

In CI-Umgebungen mit mehreren tausend Tests summiert sich diese unnötige Renderarbeit zu einer messbaren Verzögerung der gesamten Pipeline. Ein pragmatischer Ansatz ist, komplexe, tief verschachtelte Komponenten grundsätzlich mit shallowMount als Standard zu testen und mount nur dort gezielt einzusetzen, wo tatsächlich eine mehrstufige Interaktion geprüft werden muss. Dieser Standard lässt sich in vielen Projekten sogar als Team-Konvention festhalten, um Diskussionen bei jedem einzelnen Test zu vermeiden.

9. Entscheidungshilfe für die Praxis

Als grobe Orientierung hilft die Frage, was genau ein Test eigentlich beweisen soll. Geht es um die interne Logik einer einzelnen Komponente, um bedingtes Rendering, Props-Verarbeitung oder lokalen State, ist shallowMount fast immer die richtige, schnellere Wahl, weil alles außerhalb dieser Grenze bewusst ausgeblendet wird. Geht es dagegen um das Zusammenspiel mehrerer Komponenten, insbesondere über Events, Slots oder Provide/Inject hinweg, ist mount notwendig, weil genau dieses Zusammenspiel sonst gar nicht stattfindet.

Ein guter Testcode-Review achtet deshalb nicht nur darauf, ob ein Test grün ist, sondern auch darauf, ob die gewählte Mount-Strategie zur eigentlichen Testabsicht passt. Ein mount-Test, der eigentlich nur eine isolierte Eigenschaft prüft, ist unnötig langsam und fragil gegenüber Änderungen in Kindkomponenten. Ein shallowMount-Test, der eigentlich Interaktion zwischen Komponenten prüfen will, liefert dagegen ein falsches Sicherheitsgefühl, weil die eigentlich kritische Verbindung nie wirklich ausgeführt wird.

Kriterium shallowMount mount
Kindkomponenten als Stub gerendert vollständig ausgeführt
Testgeschwindigkeit schneller, konstant abhängig von Baumtiefe
Slot-Inhalte nicht automatisch sichtbar vollständig sichtbar
Provide/Inject wird nicht real geprüft wird real geprüft
Typischer Einsatz isolierte Unit-Tests Integrationstests

Mironsoft

Vue-Architektur, Composition API und Nuxt-Performance

Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?

Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.

Architektur-Review

Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.

Performance-Audit

Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.

Nuxt-Integration

SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.

10. Zusammenfassung

shallowMount vs. mount: Das Wichtigste auf einen Blick

Isolation

shallowMount stubbt alle Kindkomponenten automatisch

Vollständigkeit

mount rendert den kompletten Komponentenbaum

Slots

Slot-Inhalt von gestubbten Kindern bleibt bei shallowMount unsichtbar

Feinsteuerung

global.stubs erlaubt gezieltes Ausnahmen einzelner Komponenten

11. FAQ: shallowMount vs. mount: Das Wichtigste auf einen Blick

1Ist shallowMount grundsätzlich schneller als mount?
Ja, weil weniger tatsächlicher Rendering-Code läuft. Bei flachen Komponentenbäumen ist der Unterschied gering, bei tiefen Bäumen mit mehreren Ebenen kann er in einer großen Testsuite spürbar werden.
2Kann ich shallowMount und mount in derselben Testdatei mischen?
Ja, das ist sogar üblich. Verschiedene Testfälle innerhalb einer Datei können unterschiedliche Anforderungen an Isolation haben, deshalb ist die Wahl pro Test sinnvoll und nicht global pro Datei.
3Warum sieht mein Test mit shallowMount keinen Slot-Inhalt?
Weil der automatisch erzeugte Stub die slot-Definition der Kindkomponente nicht kennt. Mit global.stubs und einem eigenen Template für die betroffene Komponente lässt sich das gezielt beheben.
4Prüft shallowMount Provide/Inject überhaupt nicht?
Der eigene inject-Aufruf der getesteten Komponente wird geprüft, aber ob ein Wert korrekt bei einer gestubbten Kindkomponente ankommt, wird nicht real ausgeführt, weil diese Kindkomponente gar nicht läuft.
5Sollte ich als Standard immer shallowMount verwenden?
Für reine Unit-Tests einzelner Komponenten ist das ein sinnvoller Standard. Sobald das Zusammenspiel mehrerer Komponenten geprüft werden soll, ist mount die richtigere Wahl für diesen konkreten Test.
6Wie stubbe ich nur eine einzelne Kindkomponente bei mount?
Über die Option global.stubs mit dem Namen der Komponente und dem Wert true, zum Beispiel stubs: { HeavyChart: true }. So bleibt der restliche Baum vollständig gerendert.
7Wirkt sich die Wahl auf Snapshot-Tests aus?
Ja, deutlich. Ein Snapshot mit mount enthält den kompletten gerenderten HTML-Baum aller Kinder, ein Snapshot mit shallowMount nur die Stub-Tags. Änderungen in Kindkomponenten lösen dadurch bei mount unnötig viele Snapshot-Diffs aus.
8Funktioniert shallowMount auch mit async setup()?
Ja, aber nur für die getestete Komponente selbst. Asynchrone setup()-Aufrufe in Kindkomponenten werden gar nicht erst ausgeführt, weil diese Kinder als Stub ersetzt werden.
9Muss ich bei Composition-API-Komponenten anders vorgehen als bei Options API?
Nein, shallowMount und mount funktionieren für beide API-Stile identisch, weil sie auf Ebene der Komponentenrenderung ansetzen, nicht auf Ebene der internen API-Syntax.
10Gibt es eine Faustregel für neue Teams?
Mit shallowMount als Default starten und gezielt auf mount wechseln, sobald ein Test tatsächlich das Zusammenspiel mehrerer Komponenten prüfen muss, funktioniert in den meisten Projekten gut und hält die Suite von Anfang an schnell.