Entwicklung/UITests

    From The Document Foundation Wiki
    This page is a translated version of the page Development/UITests and the translation is 100% complete.
    Other languages:

    Dies ist die vorbereitende Dokumentation für den UI-Testrahmen mit speziellem Fokus auf der Phyton Seite.

    Architektur

    Das Testen der UI besteht aus zwei Teilen, einem C++-Teil, der die LibreOffice UI in einer selbstkotrolierender Library mit einem Phyton Rahmen mit dem aktuellen Test-Code verbindet. Die Verbindung zwischen den beiden Teilen ist mit einem simplen meist nicht typisiertem UNO Interface verwirklicht.

    C++ selbstkontrollierende Library

    Die C++ selbstkontrollierende Library besteht aus einer UNO API die für die Kommunikation zwischen dem Phyton Teil des Rahmens und dem Wrapper für viele UI Instanzen zuständig ist. Das UNO Interface hat nur ein paar Methoden und meistens findet die Kommunikation durch Text-Paramter statt.

    Der Wrapper für die UI Instanzen bietet die selben Methoden wie das UNO Interface plus einiger Hilfsmethoden.

    Pyhton Rahmen

    Tests erstellen

    Xisco Faulis Präsentation von der FOSDEM 2021 LibreOffice QA - how to write your first test geht durch den Prozess der Testerstellung.

    Xiscos Präsentation von der FOSDEM 2022 zeigt Things you can test in a UITest.

    Das Makefile

    Ein einfaches Makefile für den Test des UI sieht in etwa wie folgt aus: das Makefile-Demo. Der wichtigste Teil dabei ist die UITest gbuild class und dass das gb_UITest_add_module auf die Verzeichnisse mit den Testdateien zeigt.

    Normalerweise ist sonst nichts nötig und meistens wird die gbuild UITest Klasse genutzt.

    $(eval $(call gb_UITest_UITest,range_name))
    
    $(eval $(call gb_UITest_add_modules,range_name,$(SRCDIR)/sc/qa/uitest,\
        test_dir1/ \
        test_dir2/ \
    ))

    Der Parameter Bereichs-Name ist der Name des Test-Ziels. $(SRCDIR/sc/qa/uitest ist das Wurzel-Verzeichnis und die Parameter in diesem Teil beinhaltet die Verzeichnisse für die Tests.

    Optional kann der Test auch mit einer bestimmten registrymodifications.xcu Datei gestartet werden, um eine spezielle Konfiguration vorzugeben. Jeder Test hat sein eigenes User Profil in workdir/UITest/test_name/user/. Mit dem folgenden Makefile Schnippsel kann eine Datei in dieses Verzeichnis kopiert werden.

    $(eval $(call gb_UITest_use_configuration,range_name,$(SRCDIR)/sc/qa/uitest/range_name/registrymodifications.xcu))

    Dabei wird die Datei sc/qa/uitest/range_name/registrymodifications.xcu ins User Profil kopiert und der Test wird mit diesen Einstellungen durchgeführt.

    Erstellung des Python Codes

    Infrastruktur

    Kommandos für UI Elemente

    Generisch

    Status für UI-Instanzen

    Die Methode zur Ermitlung des Status wird von allen UI Elementen zur Verfügung gestellt. Die Methode gibt den Status als Sequenz von PropertyValues zurück.

    ui_element.getState()

    Das UI-Test-Framework stellt eine Methode zur Verfügung, die die zurückgegebene Sequenz von ValueProperties in ein Python Verzeichnis konvertiert.

    from uitest.uihelper.common import get_state_as_dict
    
    get_state_as_dict(ui_element)

    Tastatureingabe an ein Element übergeben

    from uitest.uihelper.common import type_text
    
    xWriterDoc = self.xUITest.getTopFocusWindow()
    xWriterEdit = xWriterDoc.getChild("writer_edit")
    type_text(xWriterEdit, "Test")

    Text auswählen

    from uitest.uihelper.common import select_text
    
    xEdit = xAddNameDlg.getChild("edit")
    select_text(xEdit, from_pos="2", to="12")

    Vergrösserung einstellen

    xWriterEdit = xWriterDoc.getChild("writer_edit")
    xWriterEdit.executeAction("SET", mkPropertyValues({"ZOOM": "200"}))

    auf eine Seite wechseln

    xWriterEdit = xWriterDoc.getChild("writer_edit")
    xWriterEdit.executeAction("GOTO", mkPropertyValues({"PAGE": "1"}))

    Steuerbefehle

    Steuerung Code Ausdruck
    Button

    xControl.executeAction("CLICK", tuple())

    ---

    TextBox

    from uitest.uihelper.common import type_pos
    
    type_text(xControl, "Text")

    self.assertEqual(get_state_as_dict(xTitleText)["Text"], "Text")

    CheckBox

    xControl.executeAction("CLICK", tuple())

    self.assertEqual(get_state_as_dict(xControl)["Selected"], "true")

    Schriftsatz Widget

    from libreoffice.uno.propertyvalue import mkPropertyValues
    
    xControl.executeAction("SELECT", mkPropertyValues({"COLUMN": "2", "ROW": "2"}))

    ---

    Baum Liste

    # Baum auf-/zuklappen
    xTreelist = xExDBDlg.getChild("availablelb")         
    xTreeEntry = xTreelist.getChild('0')
    xTreeEntry.executeAction("EXPAND", tuple())  #Available Databases
    xTreeEntry.executeAction("COLLAPSE", tuple()) #Click on the Bibliography
    
    xTreeEntry2 = xTreeEntry.getChild('0')
    xTreeEntry2.executeAction("SELECT", tuple())

    ---

    ComboBox

    from uitest.uihelper.common import select_pos
    
    select_pos(xCombobox, "1")

    self.assertEqual(get_state_as_dict(xNumbering)["SelectEntryText"], "List 1")

    Tabs

    from uitest.uihelper.common import select_pos
    
    xTabs = xDialog.getChild("tabcontrol")
    select_pos(xTabs, "0")

    self.assertEqual(get_state_as_dict(xTabs)["CurrPagePos"], "0")

    ListBox (Position)

    from uitest.uihelper.common import select_pos
    
    select_pos(xListBox, "1")

    self.assertEqual(get_state_as_dict(xListBox)["SelectEntryText"], "List 1")

    ListBox (Name)

    from uitest.uihelper.common import select_pos
    
    categoryLB = xCellsDlg.getChild("categorylb")
            props = {"TEXT": "Time"}
    
    actionProps = mkPropertyValues(props)
            categoryLB.executeAction("SELECT", actionProps)

    self.assertEqual(get_state_as_dict(categoryLB)["SelectEntryText"], "Time")

    Tabdialog (Name)

    props = {"NAME": "Borders"}
            propsUNO = mkPropertyValues(props)
            xCellsDlg.executeAction("SELECT", propsUNO)

    ---

    Spinfield

    xDecimalPlaces.executeAction("UP", tuple())
    xDecimalPlaces.executeAction("DOWN", tuple())

    self.assertEqual(get_state_as_dict(xDecimalPlaces)["Text"], "Title text")

    Radiobutton

    xSuperscript.executeAction("CLICK", tuple())

    self.assertEqual(get_state_as_dict(xSuperscript)["Checked"], "true")

    ColorListBox

    xWidget.executeAction("OPENLIST", tuple())
    xFloatWindow = self.xUITest.getFloatWindow()
    xPaletteSelector = xFloatWindow.getChild("palette_listbox")
    select_by_text(xPaletteSelector, "chart-palettes")  # or some other name
    xColorSet = xFloatWindow.getChild("colorset")
    xColorSet.executeAction("CHOOSE", mkPropertyValues({"POS": "2"}))  # or some other position

    ---

    MenuButton

    xWidget.executeAction("OPENFROMLIST", mkPropertyValues({"POS": "0"}))

    ---


    Keyword Aktionen

    Beispiel:

    from libreoffice.uno.propertyvalue import mkPropertyValues
    
    ui_object.executeAction("TYPE", mkPropertyValues({"KEYCODE":"CTRL+A"}))

    Schlüsselwörter:

    • ESC
    • TAB
    • DOWN
    • UP
    • LEFT
    • RIGHT
    • DELETE
    • INSERT
    • BACKSPACE
    • RETURN
    • HOME
    • END
    • PAGEUP
    • PAGEDOWN
    • CTRL
    • ALT
    • SHIFT

    Wie wird die introspection Library erweitert

    UI Elemente, die das vcl::Window erweitern

    andere UI Elemente

    Tools zur Erstellung eines Tests

    Die Schwierigkeit beim Testen besteht darin, welche Kommandos gesendet werden sollen und welche Aktion auf einer UI ausgelöst werden soll. Glücklicherweise ist es möglich, die UNO Kommandos zu protokollieren.

    1. Nutzen Sie diese Zeilen in .bashrc oder .cshrc:
      export PYTHONPATH=/"Pfad zum LibreOffice Datenspeicher"/instdir/program/
      export URE_BOOTSTRAP=file:///"Path of LibreOffice repo"/instdir/program/fundamentalrc
    2. LibreOffice folgendermaßen starten:
      LO_COLLECT_UIINFO="test.log" SAL_USE_VCLPLUGIN=gen instdir/program/soffice
      Hinweis: "LO_COLLECT_UIINFO" ist ein relativer Pfad unter "instdir/uitest/".
    3. Aktivitäten mit der Maus durchführen
    4. LibreOffice schließen
    5. die Ergebnisdatei in instdir/uitest/test.log öffnen
    6. ins UI Protokoll Verzeichnis wie folgt wechseln:
      cd uitest/ui_logger_dsl/
    7. Sicherstellen, dass textX library installiert wurde
    8. folgendes Kommando ausführen
      python dsl_core.py <Pfad_zur_Protokolldatei> <Pfad_zu_einer_neuen_Phyton_Datei>
      <Pfad_zur_Protokolldatei> sollte mit SourceDirectory/core/instdir/uitest/test.log oder so etwas ähnlichem ersetzt werden und <Pfad_zu_einer_neuen_Phyton_Datei> kann ein Ort Ihrer Wahl sein, an dem der generierte Code landen sollte.

    All diese Schritte können in diesem YouTube Video nachvollzogen werden:

    Bitte akzeptieren Sie dieses Video. Dadurch gelangen Sie an Inhalt von YouTube, einem Service, der von einer dritten Partei bereitgestellt wird.

    YouTube privacy policy


    Testausführung

    Bevor der Test gestartet wird, sollte sichergestellt werden, dass er funktioniert. Eine einfache Möglichkeit, alle UI-Tests im UI-Modul auszuführen, ist es, uitest.uicheck auszuführen. So können wir das UI sehen, während der Test läuft und die Tests laufen einfach durch. Dies wird erreicht, indem der Test in folgendem Core-Verzeichnis ausgeführt wird:

    (cd sc && make -srj1 UITest_sort UITEST_TEST_NAME="sorting.CalcSorting.test_Sortingbuttons_detect_columnheaders" SAL_USE_VCLPLUGIN=gen)

    Es ist auch möglich, ein Shellskript zu verwenden, um den Test zu starten.

    Dieses Skript muss im Quellverzeichnis des LibreOffice Cores laufen.

    Es können auch ein paar Umgebungsvariablen nötig sein, damit das Skript funktioniert. Daher lautet das vollständige Skript wie folgt:

    export SRCDIR=$PWD
    export PYTHONPATH=$SRCDIR/instdir/program:unotest/source/python
    export URE_BOOTSTRAP=file://$SRCDIR/instdir/program/fundamentalrc
    export TestUserDir=file:///tmp
    export TDOC=$SRCDIR/sw/qa/uitest/data
    export SAL_USE_VCLPLUGIN=gen
    export LC_ALL=C
    
    rm -rf /tmp/libreoffice/4
    
    python3 "$SRCDIR"/uitest/test_main.py --soffice=path:"$SRCDIR"/instdir/program/soffice --userdir=file:///tmp/libreoffice/4 --file="$SRCDIR"/sc/qa/uitest/sort/sorting.py

    Im Fall, dass der Test get_url_for_data_file aufruft, um eine Datei zu öffnen, ändern Sie bitte die Variable TDOC auf das Verzeichnis, in dem die Datei liegt. Wenn Sie den Test bei sichtbarem UI ausführen, werden Sie feststellen, dass der Test zu schnell läuft, um festzustellen, was vor sich geht. Eine Quick and Dirty Lösung wäre, in Pyhton time.sleep(10) zu ergänzen. Das stoppt die Ausführung ein wenig. Die andere Lösung wäre, im Code an interessanten Stellen uitest.debug.sleep zu verwenden. Bitte verwenden Sie nur ein paar nützliche Stellen im Test. Diese Methode wird ignoriert, bis der Test im Debug-Modus läuft, indem der Test mit dem Parameter --debug gestartet wird.

    Falls Sie nur einen Test im Test-Skript brauchen, verwenden Sie folgendes Kommando:

    UITEST_TEST_NAME="FILENAME.CLASS_NAME.TEST_NAME" ./execute.sh

    zum Beispiel

    UITEST_TEST_NAME="sorting.CalcSorting.test_Sortingbuttons_detect_columnheaders" ./execute.sh


    nicht unterstützte UI Bestandteile

    • Base Support
    • Folien sortieren in Impress
    • Doppelklick in Formulare. Siehe tdf#133641 für Ideen
    • Datei-Assistenten
    • Impress - Präsentationsminimierung
    • Protokollierung (LO_COLLECT_UIINFO) unter Windows
    • Doppelklick
    • Grafik-Protokollierung
    • untere Info-Toolbar in Calc
    • besserer Support für Shortcuts (tdf#124931)

    Liste der UI Tests im Master

    Siehe die Liste der bereits implementierten UITests.

    Fehler finden

    In Bugzilla gibt es das Schlüsselwort needUITest um alle Fehler aufzulisten, die einen automatischen UITest erfordern:

    Weitere Informationen