Python / Selenium Fehlermeldung zu UnicodeDecodeError

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Hallo zusammen,

momentan bekomme ich bei einem Skript immer eine Fehlermeldung die ich nicht geknackt bekomme. Ich mache gerade ein Tutorial für Selenium mit Python und ein anderes Skript, das im Aufbau sehr ähnlich ist, funtioniert reibungslos. So wie ich das verstehe funktioniert das mit der tearDown Funktion nicht. Im anderen Skript sieht es exakt geich aus und funktioniert. Deswegen meine Ratlosigkeit. Bisherige Recherechen ergaben nix. Vielleicht erkenne ich das auch nur nicht, wei ich Python Newbie bin.

Fehlermeldung:

Code: Alles auswählen

C:\Robot\untitled\SeleniumProjektAbschnitt2\venv\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3.3\helpers\pycharm\_jb_unittest_runner.py" --target test_form1_selenium_kurs_firefox.testForm1SeleniumKursFirefox
Launching unittests with arguments python -m unittest test_form1_selenium_kurs_firefox.testForm1SeleniumKursFirefox in C:\Robot\untitled\SeleniumProjektAbschnitt2\tests
+++ Initialisierung WebDriver
+++ FIREFOX Start Login Test
+++ Ausgabe: Magazzini Alimentari Riuniti
+++ Vergleich OK
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3.3\helpers\pycharm\_jb_unittest_runner.py", line 35, in <module>
    main(argv=args, module=None, testRunner=unittestpy.TeamcityTestRunner, buffer=not JB_DISABLE_BUFFERING)
  File "C:\Python27\Lib\unittest\main.py", line 95, in __init__
    self.runTests()
  File "C:\Python27\Lib\unittest\main.py", line 232, in runTests
    self.result = testRunner.run(self.test)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3.3\helpers\pycharm\teamcity\unittestpy.py", line 304, in run
    return super(TeamcityTestRunner, self).run(test)
  File "C:\Python27\Lib\unittest\runner.py", line 151, in run
    test(result)
  File "C:\Python27\Lib\unittest\suite.py", line 70, in __call__
    return self.run(*args, **kwds)
  File "C:\Python27\Lib\unittest\suite.py", line 108, in run
    test(result)
  File "C:\Python27\Lib\unittest\suite.py", line 70, in __call__
    return self.run(*args, **kwds)
  File "C:\Python27\Lib\unittest\suite.py", line 108, in run
    test(result)
  File "C:\Python27\Lib\unittest\case.py", line 393, in __call__
    return self.run(*args, **kwds)
  File "C:\Python27\Lib\unittest\case.py", line 370, in run
    result.stopTest(self)
  File "C:\Program Files\JetBrains\PyCharm Community Edition 2017.3.3\helpers\pycharm\teamcity\unittestpy.py", line 260, in stopTest
    output = sys.stdout.getvalue()
  File "C:\Python27\Lib\StringIO.py", line 271, in getvalue
    self.buf += ''.join(self.buflist)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 34: ordinal not in range(128)

Process finished with exit code 1
Ich nehme an es geht um diesen Teil der Fehlermeldung:

Code: Alles auswählen

UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 34: ordinal not in range(128)
Skript:

Code: Alles auswählen

# This Python file uses the following encoding: utf-8
import unittest

from selenium import webdriver

from pages.seleniumkurs_login_page import SeleniumKursLoginPage
from pages.selniumkurs_home_page import SeleniumKursHomePage
from pages.seleniumkurs_test_app_page import SeleniumKursTestAppPage
from pages.seleniumkurs_testform1_page import SeleniumKursTestForm1Page

class testForm1SeleniumKursFirefox(unittest.TestCase):

    def setUp(self):
        print("+++ Initialisierung WebDriver")
        self.driver = webdriver.Firefox()
        self.driver.maximize_window()
        self.driver.get("https://externeTestseite.fuerSeleniummkursde")

    def tearDown(self):
        print("+++ tearDown nach jedem Test. Aufräumen.")
        self.driver.close()

    def test_login(self):
        print("+++ FIREFOX Start Login Test")

        ## Arrange

        # Login
        loginPage = SeleniumKursLoginPage(self.driver)
        loginPage.zugangsdaten_eingeben("derBenutzername", "dasPasswort")
        loginPage.login_button_anklicken()

        # Navigation durch das Portal
        homePage = SeleniumKursHomePage(self.driver)
        homePage.hauptmenu_aufrufen()
        homePage.selenium_test_app_anklicken()

        testAppPage = SeleniumKursTestAppPage(self.driver)
        testAppPage.test_form1_anklicken()

        # Starte Formular

        testForm1Page = SeleniumKursTestForm1Page(self.driver)
        testForm1Page.betreff_eingeben("Automatischer Test")
        testForm1Page.name_eingeben("Dieter")

        testForm1Page.kurs_auswaehlen("Java Grundlagen Kurs mit Dieter")

        testForm1Page.fimra_in_box1_auswaehlen([2,4,6])
        testForm1Page.firmen_uebernehmen()

        testForm1Page.firma_in_box2_auswaehlen([2])

        testForm1Page.ausgewaehlte_Firmen_nach_oben_verschieben()

        ## Act

        testForm1Page.formular_speichern()

        ## Assert

        erfolgsmeldung = testForm1Page.statusmeldung_auslesen()
        self.assertTrue("Java Grundlagen Kurs mit Dieter" in erfolgsmeldung)

        erstesElement = testForm1Page.erstes_listenelement_auslesen()
        print("+++ Ausgabe: " + erstesElement)
        self.assertEqual(erstesElement,"Magazzini Alimentari Riuniti")
        print("+++ Vergleich OK")


    if __name__ == '__main__':
        unittest.main()
Ist zu erkennen was da nicht stimmt?

Vielen Dank und beste Grüße
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

Da stimmt nicht, dass Du Python2.7 benutzt, das wird schon sehr lange nicht mehr gepflegt. Für Python2 fehlt die Encoding-Angabe in der Datei und dass Du Unicode-Strings benutzt.
`unittest` will auch niemand freiwillig benutzen. Quasi-Standard ist pytest.
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Ja, ist alles was älter und für das offenbar ältere Tutorial wollte ich das auch so lassen. Das wurde dort halt so angegeben.

Ich dachte mit dieser Zeile ist das drin?

Code: Alles auswählen

# This Python file uses the following encoding: utf-8
Beim anderen Skript funktioniert das.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist Prosa. Du koenntest auch "Dieses Programm ist das beste & frei von Fehlern" drueber schreiben, mit dem gleichen Effekt.

Das muss mit

Code: Alles auswählen

-*- coding: utf-8 -*-
angegeben werden. Bezieht sich dann aber ausschliesslich auf u"Irgendwas"-Strings. Man beachte das u.
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Das ergibt alles keinen Sinn.

Ich habe jetzt bei der tearDown das Wort Aufräumen rausgenommen wegen dem Umlaut. etzt läuft es durch.

In dem Skript bei dem es mit diesem Wort durchläuft steht oben zuerst:

Code: Alles auswählen

# This Python file uses the following encoding: utf-8
Im vorliegendem problematischen Skript funktioniet diese Angabe nicht, auch nicht mit

Code: Alles auswählen

# -*- coding: utf-8 -*-
Es kommt explizit an dieser Stelle wegen Umlaut zum Problem.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst schon die Fehlermeldung zeigen. Und das oben angegebene Encoding muss auch dem entsprechen, in dem das File vorliegt. Diese Deklaration am Kopf ist fuer den Python-Parser, der dann die String-Literale, die mit u anfangen (oder den __future__-import haben, dass all Strings unicode sind), dazu bringt, die eben mit dem utf-8 encoding zu dekodieren.

Das hat aber nichts damit zu tun, wie dann die tatsaechlichen Bytes in der Datei beschaffen sind. Das ist die Aufgabe des Editors bzw. des Benutzers eben dieses Editors, sicherzustellen, dass das auch wirklich utf-8 *ist*.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nur mal kurz als Gegenprobe: 0xc3 ist der richtig Anfang fuer klein ae in utf-8

Code: Alles auswählen

>>> s = "Aufräumen"
>>> s.encode("utf-8")
b'Aufr\xc3\xa4umen'
>>> 
Die Fehlermeldung von oben erwaehnt ascii, das sollte jetzt utf-8 sein. Wenn nicht, ist es immer noch falsch angegeben.
Sirius3
User
Beiträge: 17738
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: ich habe es nochmal nachgelesen. Python ist da sehr großzügig. Es muß nur den regulären Ausdruck `^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)` erfüllen, also geht auch

Code: Alles auswählen

# Everything I do is miscoding: utf-8
@pixelhead: und wie ich oben schon erwähnt hatte, braucht Python2 Unicode-Strings:

Code: Alles auswählen

print(u"+++ tearDown nach jedem Test. Aufräumen.")
Das beantwortet aber immer noch nicht die Frage, warum Du überhaupt Python2 benutzt.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Sirius3: hah, interessant.
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Danke!

Damit (ganz oben, erste Zeile)

Code: Alles auswählen

# -*- coding: utf-8 -*-
und damit

Code: Alles auswählen

print(u"+++ tearDown nach jedem Test. Aufräumen")
geht es.

Danke!
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Das beantwortet aber immer noch nicht die Frage, warum Du überhaupt Python2 benutzt.
Ich habe das so installiert wie das im Tutorial angegeben wurde. Ich nehme an das Tutorial ist was älter ... ich fing damit vor längerer Zeit an (vor ein oder zwei Jahren?) und musste dann unterbrechen. Ob Python2 da schon alt war, weiß ich nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13073
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pixelhead: Das Tutorial scheint eigentlich für Java zu sein. Der Code sieht so was von „unpythonisch“ und so krass nach Javaprogrammierer aus, dass der Test offenbar auch die passende Kursseite auswählt:

Code: Alles auswählen

        testForm1Page.kurs_auswaehlen("Java Grundlagen Kurs mit Dieter")
Es gibt anscheinend eine Datei pro Klasse. Das ist Java und nicht Python. Die Namensschreibweise hält sich nicht an die Python-Konventionen (was `unittest` selbst auch nicht macht). Und es wird diese gruselige JUnit-API verwendet. Die hat sich mittlerweile ein wenig verbessert, aber bei Python 2 hat man davon natürlich noch nichts. Wie schon erwähnt wurde ist Pytest ein verbreitetes „pythonisches“ Test-Rahmenwerk.

Das Wort Seleniumkurs kommt viel zu oft in Namen vor. Das sollte zum Beispiel nicht Präfix von den ganzen Modulen und Klassen sein. Falls das tatsächlich einen Namensraum darstellen soll, dann sollte es ein Package sein, falls nicht und es sich bei dem Projekt um einen Seleniumkurs handelt, muss das nicht in jedem fucking Namen noch mal stehen. Dann heisst das Projekt ja schon so.

Das Projekt/Gesamtpackage wird ja eher nicht `pages` heissen, womit was mit den Importen ”nicht stimmt”. Diese Art von stillschweigend relativen Importen macht man nicht mehr. Entweder explizit relativ, oder absolut. Ich persönlich bevorzuge in der Regel letzteres.

Die `print`-Ausgaben sind überflüssig. Die Informationen gibt jeder brauchbare Testrunner aus. (Wenn man ``print`` wie eine Funktion schreibt, sollte man in Python 2 auch ``from __future__ import print_function`` als ersten Import schreiben.)

Die `test_login()`-Methode macht offenbar mehr als nur die Anmeldung zu testen. Entweder stimmt der Name nicht, oder der Inhalt.

Der Code sähe mit Pytest dann eher so aus:

Code: Alles auswählen

import pytest
from selenium import webdriver
from seleniumkurs.pages import HomePage, LoginPage, TestAppPage, TestFormPage


@pytest.fixture(name="driver")
def create_driver():
    with webdriver.Firefox() as driver:
        driver.maximize_window()
        driver.get("https://externeTestseite.fuerSeleniummkursde")
        yield driver


def test_login(driver):
    login_page = LoginPage(driver)
    login_page.zugangsdaten_eingeben("derBenutzername", "dasPasswort")
    login_page.login_button_anklicken()

    home_page = HomePage(driver)
    home_page.hauptmenu_aufrufen()
    home_page.selenium_test_app_anklicken()

    test_app_page = TestAppPage(driver)
    test_app_page.test_form1_anklicken()

    test_form_page = TestFormPage(driver)
    test_form_page.betreff_eingeben("Automatischer Test")
    test_form_page.name_eingeben("Dieter")

    test_form_page.kurs_auswaehlen("Java Grundlagen Kurs mit Dieter")

    test_form_page.fimra_in_box1_auswaehlen([2, 4, 6])
    test_form_page.firmen_uebernehmen()

    test_form_page.firma_in_box2_auswaehlen([2])

    test_form_page.ausgewaehlte_Firmen_nach_oben_verschieben()

    test_form_page.formular_speichern()

    erfolgsmeldung = test_form_page.statusmeldung_auslesen()
    assert "Java Grundlagen Kurs mit Dieter" in erfolgsmeldung
    erstes_element = test_form_page.erstes_listenelement_auslesen()
    assert erstes_element == "Magazzini Alimentari Riuniti"
Wobei man wohl einiges was in Deinem Quelltext unter ``## Arrange`` steht wohl noch in weitere Fixtures auslagern würde. Je nach dem welche Teilergebnisse davon von anderen Tests auch benötigt werden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
pixelhead
User
Beiträge: 12
Registriert: Donnerstag 27. Oktober 2022, 16:35

Danke für die Ausführungen.

Ja, es ist ein Seleniumkurs und er behandelt Java und Python. Es kann gut sein, dass hier ein hauptsächlicher Javamensch den Kurs macht und Python quick and dirty hinzugefügt hat. Das kann ich mit meinen Kenntnissen aber nicht beurteilen, deswegen danke für die Hinweise.

Ich bin neu in der Programmierung und fand bisher hat mir der Kurs so einiges gezeigt.

Alles dauernd Seleniumkurs zu nennen fand ich auch blöd, aber ich hab es mitgemacht, weil ich nicht wusste was späte kommt und unbedingt die Funktionen usw. gleich wiedererkennen wollte wie er sie benutzt. Bei konsequenter besserer Benennung wäre ich locker durcheinander gekommen. (Nehme ich an).

Die Methode weiterhin `test_login()` zu nennen fand ich auch unschön. Das wurde aus einer vorherigen Datei wohl übernommen und dann alles was folgt geändert. Wenn man auf Kopien aufbaut ..
Antworten