Add testing for bloc

pull/3/head
Jonas Franz 2 years ago
parent d967c3b767
commit 4332fd88bf
  1. 8
      chapters/conclusion/conclusion.tex
  2. 49
      chapters/evaluation/bloc.tex
  3. 10
      chapters/evaluation/evaluation.tex
  4. 2
      chapters/evaluation/inheritedwidget.tex

@ -1 +1,7 @@
\chapter{Fazit}
\chapter{Fazit}
Ausblick:
Austausch der MI-Metrik
Erweiterung um andere Statemanagement-Systeme
Vergleich mit React?

@ -1,20 +1,63 @@
\section{BLoC}
\label{eval:bloc}
\acl{bloc} sind ein weit verbreitetes Konzept zur Zustandsverwaltung. Die Grundlagen zu diesem Zustandsverwaltungssystem wurden bereits in \autoref{sec:bloc} eruiert.
\subsection{Implementierung}
Für die Implementierung der App mit \ac{bloc} wurden drei \acl{bloc} erstellt. Diese verwalten den den Anmeldezustand, den Inhalt des Warenkorbs sowie die verfügbaren Produkte. Die \ac{bloc}s bestehen dabei aus zu teils mehreren \texttt{Stream}s und \texttt{Sink}s, die Zustandsänderungen von außen entgegennehmen und nach außen kommunizieren. Zusätzlich dazu gibt es für die \texttt{Stream}s öffentliche Variablen, welche den aktuellen Zustand wiedergeben. Dies dient, wie auch im Beispiel \autoref{lst:streambuilder} anhand des Parameters \texttt{initialData} zu sehen ist, dazu, dass Widgets, die erst später zum Widget-Tree hinzugefügt werden den aktuellen Zustand zum Zeitpunkt des initialen Erstellens erhalten können, da die Streams immer nur Zustandsänderungen kommunizieren können.
Alle drei \ac{bloc}s werden über ein Widget an die Benutzeroberfläche weitergegeben, welches als Dependency Injection fungiert. Hier wären auch andere Implementierungsvarianten beispielsweise mit dem Service Locator \texttt{get\_it} denkbar gewesen. Der gewählte Ansatz benötigt jedoch keine zusätzliche Bibliothek und verfälscht somit das Ergebnis am wenigsten. Die Widgets der Benutzeroberfläche verwenden dabei \texttt{StreamBuilder}, wie in \autoref{lst:streambuilder} zu sehen ist, um über aktualisierten Zuständen informiert zu werden.
\begin{lstlisting}[caption={Verwendung eines StreamBuilder in cart\_button.dart \cite{repo}}, label={lst:streambuilder}]
final cartBloc = AppState.of(context).blocProvider.cartBloc;
return StreamBuilder<int>(
stream: cartBloc.numberOfProductsStream,
initialData: cartBloc.numberOfProducts,
builder: (context, snapshot) {
BenchmarkCounters.cartButton++;
return ElevatedButton.icon(
onPressed: () => Navigator.of(context).pushRouteKey(RouteKey.cart),
icon: const Icon(Icons.shopping_basket),
label: Text('Warenkorb (${snapshot.requireData} Produkte)'),
);
},
);
\end{lstlisting}
\subsection{Bewertung}
Im folgenden Abschnitt wird die Implementierung mit InheritedWidget \autocite[branch=inheritedwidget]{repo} anhand der definierten Bewertungskriterien bewertet.
Im folgenden Abschnitt wird die Implementierung mit BLoC \autocite[branch=bloc]{repo} anhand der definierten Bewertungskriterien bewertet.
\paragraph{\nameref{sec:changeablility}}
\paragraph{\nameref{sec:testability}}
Zu \ac{bloc} lässt sich sagen, dass diese sowohl skalierbar als auch änderbar sind. Diese Aussage stützt sich darauf, dass durch die Kommunikation ausschließlich über \texttt{Stream}s und \texttt{Sink}s sich eine einheitliche Schnittstelle schaffen lässt, an die beliege Komponenten sich andocken können. Anders als bei den Widget-basierten Ansätzen wie InheritedWidget lassen sich \ac{bloc} komplett plattformunabhängig einsetzen und sind somit für eine weitere Skalierung außerhalb des Flutter-Frameworks durchaus geeignet.
Die Koppelung verschiedener Zustände lässt sich somit auch einfach lösen, indem andere \ac{bloc} beispielsweise über den Konstruktor oder ein Dependency-Injection Tool übergeben werden.
Aufgrund dieser Eigenschaften wird die Bewertung \textquote{vollständig erfüllt} vergeben.
\paragraph{\nameref{sec:testability}} Zur Testbarkeit von \ac{bloc} lässt sich Folgendes sagen. Die Geschäftslogik der einzelnen \ac{bloc} lässt sich gut testen, da das Flutter-Testing-Framework bereits Werkzeuge zum Überprüfen von Streams bereithält, wie sie im Test in \autoref{lst:testbloc} verwendet werden. Hier lassen sich somit auch einfach Unit-Tests verwenden. Falls \ac{bloc} Abhängigkeiten zu anderen \ac{bloc} oder Komponenten haben, müssen diese allerdings gemockt werden. Ohne eine entsprechende Bibliothek entstehen hier zusätzliche Aufwände durch das Implementieren von Mock-Klassen.
\begin{lstlisting}[caption={Test des Cart BLoC in cart\_bloc\_test.dart \cite{repo}}, label={lst:testbloc}]
test('test cart bloc', () async {
const productBlocMock = ProductBlocMock();
final cartBloc = CartBloc(productBlocMock);
final expectedCart = {demoProducts.first: 1};
expectLater(cartBloc.cartStream, emits(expectedCart));
cartBloc.quantityEventSink.add(IncreaseQuantityEvent(demoProducts.first));
});
\end{lstlisting}
Ein weiterer betrachteter Aspekt beim Testen ist die Testbarkeit von Widgets, die auf \ac{bloc} zugreifen. Hierzu lässt sich sagen, dass das Ersetzen durch Platzhaltern ohne Probleme möglich war. Allerdings hängt dies auch von dem verwendeten Injection-System ab. In diesem Beispiel wurden InheritedWidgets verwendet. Daher kann dies äquivalent zu dem dort beschriebenen Beobachtungen (vgl. \autoref{par:ihtesting}) gewertet werden. Zusätzlich dazu lässt sich sagen, dass für das Erzeugen eines Platzhalters (engl. mock) für ein \ac{bloc} die Klasse dieses \ac{bloc} vollständig gemockt werden muss. Hier lassen sich wie im vorhergehenden Absatz beschrieben, mehrere Strategien nutzen.
Aufgrund der einfachen Testbarkeit der Geschäftslogik sowie des möglichen Austausches mit Platzhaltern für Widget-Tests wird die Testbarkeit mit \textquote{vollständig erfüllt} bewertet.
\paragraph{\nameref{sec:efficiency}} Nach der Ausführung der Teststrecke, ergaben die Zähler folgendes Ergebnis:
\lstinputlisting[caption={Anzahl der Render-Vorgänge bei BLoC}]{results/bloc/benchmarks.txt}
\paragraph{\nameref{sec:complexity}} Die Auswertung der Metriken (vgl. \autoref{metrics:bloc}) ergab eine \ac{mi} von xx für das gesamte Projekt.
\paragraph{\nameref{sec:complexity}} Die Auswertung der Metriken (vgl. \autoref{metrics:bloc}) ergab eine \ac{mi} von 82 für das gesamte Projekt.
\paragraph{\nameref{sec:readability}}

@ -31,9 +31,15 @@ Dieser Ansatz konnte nicht die Mindestanforderungen an die Beispielanwendung ums
\rot{Dokumentierung} & \rot{Strukturbestimmung} \\
\midrule
\endhead
ohne Zustandsverwaltung & n. a. & n. a. & 1;2 & 83 & n. a. & n. a. & n.a. \\
setState & \multicolumn{7}{c}{nicht umsetzbar} \\
InheritedWidget & / & \cmark & 8;6 & 83 & \xmark & / & \xmark \\
BLoC & TBD & TBD & TBD & TBD & TBD & TBD & TBD \\
\ac{bloc} & \cmark & \cmark & 8;4 & 82 & TBD & TBD & TBD \\
Provider & TBD & TBD & TBD & TBD & TBD & TBD & TBD \\
Riverpod & TBD & TBD & TBD & TBD & TBD & TBD & TBD \\
Redux & TBD & TBD & TBD & TBD & TBD & TBD & TBD \\
MobX & TBD & TBD & TBD & TBD & TBD & TBD & TBD \\
\bottomrule
\multicolumn{8}{c}{Legende: \cmark=vollständig erfüllt; /=teilweise erfüllt; \xmark=nicht erfüllt}
\multicolumn{8}{c}{Legende: \cmark=vollständig erfüllt; /=teilweise erfüllt; \xmark=nicht erfüllt;}\\
\multicolumn{8}{c}{n.a. = nicht anwendbar}
\end{longtable}

@ -43,7 +43,7 @@ testWidgets('test cart store', (tester) async {
expect(cartStore.amountOfProduct(demoProducts.first), 1);
});
\end{lstlisting}
\label{par:ihtesting}
Die Tests für Widgets, die den Zustand konsumieren auf der anderen Hand sind einfach umzusetzen, da die Geschäftslogik im besten Fall komplett von der Speicherung der emittierten Daten getrennt wird. So ist es möglich ein InheritedWidget ohne das dazugehörige StatefulWidget zu initialiseren und die benötigten Mock-Daten über den Konstruktor zu übergeben.
Abschließend erfolgt hier die Bewertung mit \textquote{vollständig erfüllt}, da besonders die einfache Testbarkeit von konsumierenden Widgets die Nachteile bei dem Test für die Geschäftslogik ausgleicht.

Loading…
Cancel
Save