« Le site web, le blog et les projets de Stéphane Klein »
stephane-klein.info est le site web d'un développeur passionné par les Logiciels Libres, les méthodologies (agiles…), la programmation en Python… mais aussi la politique, l'histoire, la philosophie…
Le site stephane-klein.info est visible sur
tout type de navigateur. Il est possible d'accéder parfaitement à toutes les
informations du site (sauf les commentaires) même avec un navigateur du type
Lynx ou
Links.
Il est conseillé d'utiliser un navigateur récent et conforme aux standards pour
consulter le site avec la totalité de son design (voir un screenshot du site…).
J'ai plus ou moins suivi le contenu de la documentation…
Dans la vidéo j'ai fait quelques erreurs d'expression alors soyez indulgents… ce n'est pas facile de faire un
screencast valide du premier coup.
Je vous conseille de visualiser la vidéo en mode 720p afin de bien pouvoir lire le contenu de la console.
Après la réalisation du screencast je me suis rendu compte que j'aurais dû agrandir la taille des polices.
Je viens de commencer à utiliser Selenium pour tester des formulaires, divers
pages de listes de données… dans une application métier.
Mes objectifs :
tester l'ajout de nouvelles entités (1)
les données sont injectées dans un formulaire d'une page html
une page de résultat est utilisées pour vérifier que les données ont bien
été enregistré (ça peut être une page d'édition)
tester la modification d'une entités (2)
les données sont injectées dans un formulaire d'une page html
une page de résultat est utilisées pour vérifier que les données ont bien
été enregistré (ça peut être une page d'édition)
tester la validité d'une page de liste (3)
tester la validité des résultats d'un moteur de recherche (4)
Pour le moment je me suis concentré uniquement sur les points 1 et 2.
Première méthode (à l'arrache)
J'ai commencé par réaliser des fonctions du type :
login()
add_customer(data)
edit_customer(data)
check_customer(data)
add_customer(data)
edit_customer(data)
check_customer(data)
…
Les fonctions de type "add" et "edit" prennent en paramètre des données à injecter dans des pages.
Les fonctions de type "add" correspondent aux pages d'ajout d'entités, les fonctions de
type "edit" correspondent aux pages de modification d'entités.
Ensuite j'ai des fonctions "check", là aussi je passe en paramètre des données qui seront
utilisées comme valeur de vérification face à des pages de résultats ou pages d'éditions (une
page d'édition contient déjà des données, le but ici est de vérifier leurs validitées).
Dans mes fonctions ("add", "check"…) j'ai une boucle qui parcourt la structure de données
et utilise les fonctions suivantes soit pour injecter des données, soit pour tester la validité
des données.
def inject_value(driver, name, value):
element = driver.find_element_by_id(name)
if element.tag_name == 'input':
if element.get_attribute("type") == "checkbox":
if element.is_enabled() != value:
element.click()
else:
element.clear()
element.send_keys(value)
elif element.tag_name == 'select':
option_element = element.find_element_by_xpath(".//option[@value='%s']" % value)
option_element.click()
elif element.tag_name == 'textarea':
element.clear()
element.send_keys(value)
def check_value(driver, name, value):
element = driver.find_element_by_id(name)
if element.tag_name == 'input':
if element.get_attribute("type") == "checkbox":
return element.is_enabled() == value
else:
return element.get_attribute("value") == value
elif element.tag_name == 'select':
return element.get_attribute("value") == value
elif element.tag_name == 'textarea':
return element.text == value
Pour le moment, cela fonctionne correctement mais je trouve mon code fastidieux pour plusieurs
raisons :
j'aimerais pouvoir définir des valeurs par défaut pour les formulaires
j'aimerais pouvoir choisir d'autres types de "selecteur", pour le moment je fais des
recherches uniquement par ID
j'aimerais pouvoir facilement indiquer le type de champ, car pour le moment je fais de
l'auto détection… mais cela ne sera pas toujours faisable
Ce que j'aimerais avoir
À noter que ce code n'est pas complet… c'est un brouillon.
from sealchemy import Form, TextField, SelectField, BooleanField
...
class AddCustomer(Form):
__submit__ = Submit(name="_same")
reference = TextField(default=u"C1345")
type_user = SelectField(default=u"external")
firstname = TextField(required=True)
lastname = TextField(required=True)
activated = BooleanField(default=True)
comment = TextAreaField(default=u"")
def go_to_page(self):
self.driver.get("/customers/add/")
class EditCustomer(Form):
__submit__ = Submit(name="_same")
reference = TextField(default=u"C1345")
type_user = SelectField(default=u"external")
firstname = TextField(required=True)
lastname = TextField(required=True)
activated = BooleanField(default=True)
comment = TextAreaField(default=u"")
def go_to_page(self, id):
self.driver.get("/customers/%s/" % id)
def go_to_last_inserted(self):
"""Va sur la page du dernier client qui a été ajouté"""
...
Cela ressemble beaucoup à l'API de wtforms que j'utilise
dans mon projet. Cela ressemble aussi à FormAlchemy que
j'aime aussi.
__submit__ permet d'indiquer le champ à utiliser par la commande submit.
Note : je n'utilise pas une seule classe pour faire mes traitements "add" et "edit" car
les formulaires d'ajouts et d'édition sont en pratique souvent différents.
L'interface de la classe de type Form :
class IForm(zope.interface.Interface):
def inject():
"""Cette méthode injecte les données vers le formulaire HTML"""
def submit():
"""Cette méthode lance le submit du formulaire"""
def inject_and_submit():
"""Exécute inject et ensuite submit"""
def check():
"""Cette méthode retourne True si les données correspondent aux
données présentes dans le formulaire HTML"""
def clear():
"""Réinitialise la valeur de tous les champs de l'instance avec
les valeurs par défauts"""
def populate(values):
"""Affecte des valeurs aux champs de l'objet."""
Quelles sont les motivations qui m'ont poussé à réaliser cette page ?
je trouve intéressant de savoir quels sont les outils que X ou Y utilise au
quotidien… on y découvre souvent des choses intéressantes (donc n'hésitez pas à réaliser
ce type de page). Un site qui a le même esprit est « Ils utilisent ça ».
il arrive que l'on me demande quelle librairie j'ai choisi pour réaliser telle ou telle tâche
parfois, je ne sais plus quelle librairie javascript j'ai choisi pour réaliser une tâche, dans ce
cas cette page me sert de "pense-bête"
Depuis 3 semaines, j'utilise de plus en plus régulièrement virtualenvwrapper.
Cet outil est vraiment très pratique. Un petit résumé rapide d'utilisation (mais
je vous conseille fortement de lire la documentation pour en savoir plus sur
les divers fonctionnalités de l'outil, comment l'installer…) :
Je crée le dossier qui contiendra mon projet :
$ mkdir ~/projets/mon_projet/
Je crée un nouvel environement qui porte le même nom que mon projet :
À noter que les commandes fournies par virtualenvwrapper supportent pour la plupart l'auto complétion,
par exemple workon <tab> affiche les environements disponibles sur votre système.
Consultez la documentation de virtualenvwrapper pour en savoir plus.
Mozilla Sync
Depuis que je suis passé à Firefox 4,
j'utilise Firefox Sync pour centraliser mes bookmarks.
Avant cela, j'utilisais avec plaisir delicious mais
l'extension delicious pour Firefox 4 n'est pas disponible.
En tout les cas, Firefox Sync fonctionne vraiment très
bien, c'est très facile à configurer, c'est totalement transparent à l'usage… un très beau travail,
réalisé entre autre par Tarek Ziade pour la partie serveur (codé en Python).
Pyramid doesn't provide enough "rails" to make it possible to
integrate truly honest-to-god, download-an-app-from-a-random-place
and-plug-it-in-to-create-a-system "pluggable" applications.
Because Pyramid itself isn't opinionated (it doesn't mandate a
particular kind of database, it offers multiple ways to map URLs
to code, etc), it's unlikely that someone who creates something
"application-like" will be able to casually redistribute it
to J. Random Pyramid User and have it "just work" by asking him
to config.include a function from the package.
This is particularly true of very high level components such
as blogs, wikis, twitter clones, commenting systems, etc.
The "integrator" (the Pyramid developer who has downloaded a
package advertised as a "pluggable app") will almost certainly
have made different choices about e.g. what type of persistence
system he's using, and for the integrator to appease the
requirements of the "pluggable application", he may be required
to set up a different database, make changes to his own code
to prevent his application from "shadowing" the pluggable
app (or vice versa), and any other number of arbitrary
changes.
Au niveau "fonctionnel" aussi Pyramid est sans opinion :
choisissez les urls que vous voulez
choisissez les modèles de template que vous voulez
choisissez les principes de fonctionnement de votre application comme vous
voulez…
Pour toutes ces raisons, le framework Pyramid n'est pas directement "Pluggable".
For this reason, we claim that Pyramid has "extensible" applications,
not pluggable applications.
Pyramid permet de réaliser des applications extensibles mais le
framework n'est pas en lui même "Pluggable".
Dans cet extrait, on a l'impression que Pyramid ne permet pas de créer des applications
"Pluggable", mais cela ne correpond pas à ce qu'explique le mail (il faut
lire la suite du mail).
Autre extrait :
Any Pyramid application can be extended without forking it as long
as its configuration statements have been composed into things
that can be pulled in via "config.include".
Dit autrement : les outils fournis par Pyramid permettent de réaliser
des applications extensibles, elles peuvent être fortement modifié sans
avoir besoin de les forker.
Bien souvent, même dans des applications biens développées en programmation
orienté objet, il est nécessaire de modifier une classe A utilisée par une autres
classes B. Pour que la classe B utilise votre nouvelle classe A, vous êtes
obligé d'effectuer des changements dans le code source de l'application…
par conséquent vous êtes obligé de forker.
Grâce à la Zope Component Architecture (ZCA)
vous pouvez effectuer cette modification sans modifier le code source de
l'application, il suffit de lui dire d'utiliser une autre classe via le
système de configuration de la ZCA.
Autre extrait :
Truly pluggable applications need to be created at a much higher level
than a web framework, I fear, as no web framework can offer enough
constraints to really make them "work out of the box".
En effet, il est possible (sera possible) de trouver des plugins pour
des applications (basées sur le framework Pyramid ) et non pas directement
des plugins basés sur le framework Pyramid .
Les applications devront faire des choix / avoir des opinions (template,
couches modèles…), cela permettra la mise à disposition de plugins
facilement installables.
D'un point de vu personnel, c'est ce type de choix que j'ai fait au sein
de la société IS-Webdesign où je travaille.
Pour pouvoir réaliser une application facilement
extensible et mettre en place un système de plugins, j'ai choisi le
framework Pyramid (avant cela Repoze BFG ).
Autre extrait :
It would be a noble goal to build an application with Pyramid that
provides these constraints and which truly does offer a way to plug
in applications (Joomla, Plone, Drupal come to mind).
Intéressant, je suis curieux de voir quand va apparaitre la première
application de ce type. À noter que j'ai déjà vu des mails à propos de
ce type de projet sur la mailing list de Repoze (mais je ne sais plus où).
C'est à ce niveau qu'intervient l'équipe du TurboGears qui
a décidé aussi de rejoindre le mouvement Pylons… et plus particulièrement Pyramid.
Le but de TurboGears est justement d'avoir des "opinions" et par conséquent
de permettre la mise à disposition de plugins.
Ici l'on peut voir comment il est facile d'ajouter un utilisateur, un domaine…
Défaut de "heroku" ?
À mes yeux heroku a un défaut rédibitoire : la plateforme n'est pas libre.
Je ne peux pas l'installer sur mes serveurs.
Pour utiliser heroku il est obligatoire de passer par leur plateforme l'hébergement.
Cependant, je vois heroku comme un modèle à suivre.
Est-ce que cela existe en Python ?
Un exemple à suivre ? en effet et plusieurs solutions dans le monde Python ont vu le jour :
djangy pour du déploiement Django, vraiment semblable à heroku
ep.io pour du déploiement WSGI (« We take your Django, Flask, Pylons or other WSGI code »)