SlideShare une entreprise Scribd logo
1  sur  126
Télécharger pour lire hors ligne
MEILLEURES PRATIQUES
DE DÉVELOPPEMENT
GRÂCE À GWT, GWTP ET JUKITO
CHRISTOPHER VIEL
CHRISTIAN GOUDREAU
Département de Génie Informatique
Université de Sherbrooke
Juin 2015
Christopher
Viel
Lead Développeur
Arcbees
+ChristopherVielArcbees
Christian
Goudreau
BEO
Arcbees
+ChristianGoudreau
@imchrisgoudreau
Partie 1
Présentation d’Arcbees
Partie 2
Résumé du MVP Model-View-Presenter
Partie 3
Survol de GWTP Framework MVP pour GWT
Partie 4
Communication serveur Rest-Dispatch
Slides disponibles à https://goo.gl/PBzmwL
Partie 5
Tests unitaires Exemples avec Mockito et Jukito
Partie 6
Meilleures pratiques
Partie 7
GWT 3.0
Slides disponibles à https://goo.gl/PBzmwL
Partie 1
Arcbees
Architectes de la Simplicité
Partie 1 Arcbees
Partie 1 Arcbees
Notre
mission
Créer des produits permettant à nos clients,
nos abeilles et la communauté
de développeurs d'optimiser leurs processus,
habitudes et façons de penser,
en créant continuellement de la valeur et en
transformant la façon dont ils travaillent.
Partie 1 Arcbees
Origine
de Arcbees
Afin d'améliorer leur efficacité
de production, les deux cofondateurs ont
conçu GWT-Platform, un outil open source
de création d'applications web doté
d'une incroyable capacité d'adaptation
accélérant la vitesse de développement
des applications.
Partie 1 Arcbees
Évolution
rapide
Rapidement, GWT-Platform a généré
une importante demande de soutien
de la part de ses utilisateurs
et les deux entrepreneurs n'ont eu d'autres
choix que de créer officiellement Arcbees
le 16 juillet 2010 afin de répondre
à ces requêtes.
Partie 1 Arcbees
Reconnaissance
de Google
En moins d'un an, Google a remarqué
la jeune entreprise qui a été invitée à
donner une conférence au Google I/O,
puis, en 2012, Christian a été nommé
sur le Comité de direction de GWT,
groupe en charge de la direction
et de l'évolution du framework.
Partie 1 Arcbees
Partie 1 Arcbees
Partie 2
MVP
Model View Presenter
➢ Model : Définit les données qui seront ultimement
gérées dans l’interface utilisateur.
➢ View : Affiche le modèle pour l’utilisateur.
Généralement passive: seulement de la logique d’
affichage. Délègue les interactions utilisateur
(events) au presenter.
➢ Presenter : Récupère et met à jour le modèle.
Interprète le modèle les données pour
les transférer à la vue.
Partie 2 MVP
M
V
P
Caractéristiques
du MVP
➢ Couplage faible entre
les différentes couches.
➢ Flot très simple,
presque linéaire.
➢ Communique exclusivement
au moyen d’interfaces.
➢ Vue passive. Ne prend
aucune décision et notifie le
presenter.
Partie 2 MVP
Partie 2 MVP
M
V
P
Partie 2 MVP
MVC
comparé
à MVP
Quand l’utiliser
➢ Idéal lorsqu’un interface utilisateur est
nécessaire pour contrôler l’application.
➢ Pratique lorsqu’il y a beaucoup d’édition
de données (formulaires, CRUD, etc.)
➢ Simple à mettre en place lorsqu’une
communication entre client - serveur
est requise.
Partie 2 MVP
Quand
l’utiliser
Quand l’utiliser
➢ Simple à tester : Le faible
couplage permet de bien
isoler les couches.
➢ Réutilisation de code plus aisée :
Le flot plus simple et l’utilisation
intensive d’interfaces permet de
mieux extraire le code
réutilisable.
Partie 2 MVP
Les bénéfices et
les désavantages
Quand l’utiliser
➢ Flexible : Il est facile de
remplacer n’importe quel
composant
tant qu’on respecte le contrat
défini par les interfaces.
➢ Peut se combiner à d’autres
design patterns.
Partie 2 MVP
Les bénéfices et
les désavantages
➢ Le grand nombre d’interface à
gérer augmente la complexité du
code. Les IDEs modernes aident
néanmoins à atténuer ce
problème (détection des
implémentations).
➢ La vue passive introduit
beaucoup de code boilerplate:
beaucoup d’aller-retour entre le
presenter et la vue.
Partie 2 MVP
Les bénéfices et
les désavantages
Partie 3
Survol de GWTP
Framework MVP pour GWT
➢ Repose sur GWT et GIN (GWT INjection).
➢ Abstraire la gestion du pattern MVP,
mais aussi imposer son utilisation.
➢ Abstraire la gestion cliente: initialiser l’
application, navigation, communication
avec le serveur.
➢ Réduire le code à écrire habituellement
requis en GWT.
➢ Optimisations: Proxy, code splitting...
GWTP
Partie 3 GWTP
➢ Permet un découplage plus poussé et
rend le code plus modulaire (DIP, IoC).
○ Aide à atteindre de meilleures
pratiques,
○ Classes immutables,
○ Créer de nouvelles dépendances
devient plus simple : Extraction de
code vers des collaborateurs (SRP).
➢ Tend à rendre les tests plus simples:
Jukito, GuiceBerry…
➢ En bref : Aide à atteindre les principes
SOLID
Injection
de
dépendances
Partie 3 GWTP
➢ Limiter le code redondant
à écrire qui est habituellement
requis en GWT.
➢ Séparer les phases d’initialisation
en phases logiques simple
à étendre ou remplacer.
➢ Permet d’utiliser l’injection
de dépendances plus tôt dans
le programme.
➢ Configuration initiale à un seul
endroit.
Références : DefaultModule, PreBootstrapper, Bootstrapper
Initialisation
de l’application
Partie 3 GWTP
Exemple: Initialisation et Bootstrapper
Configuration via le module GWT
public class ClientModule extends AbstractGinModule {
@Override
protected void configure() {
install(new DefaultModule.Builder()
.defaultPlace(NameTokens.LOGIN)
.errorPlace(NameTokens.LOGIN)
.unauthorizedPlace(NameTokens.UNAUTHORIZED)
.build());
install(new ApplicationModule());
bind(ResourceLoader.class).asEagerSingleton();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<module>
<inherits name="com.gwtplatform.mvp.MvpWithEntryPoint"/>
<source path="client"/>
<source path="shared"/>
<set-configuration-property name="gwtp.bootstrapper"
value="com.arcbees.demo.client.DefaultBootstrapper"/>
<extend-configuration-property name="gin.ginjector.modules"
value="com.arcbees.demo.client.ClientModule"/>
</module>
Bootstrapper (facultatif!)
Configuration via GIN
public class DefaultBootstrapper implements Bootstrapper {
private final PlaceManager placeManager;
@Inject
DefaultBootstrapper(PlaceManager placeManager) {
this.placeManager = placeManager;
}
@Override
public void onBootstrap() {
// Initialize states then reveal the app:
placeManager.getCurrentPlaceRequest();
}
}
➢ Permet de réagir à différents
stimuli provenant d’ailleurs
dans l’application.
➢ Permet d’éviter de coupler des
classes qui n’ont aucun point
commun.
➢ Implémentation générique
et de l’Observer.
Références : addRegisteredHandler, addVisibleHandler, @ProxyEvent
Plus de détails: https://blog.arcbees.com/?p=699
Évenements
(GWT, EventBus)
Partie 3 GWTP
➢ Isoler le comportement
du presenter pour
chaque phase de sa vie.
➢ Comportements isolés
donc plus simple à
tester unitairement.
Lifecycle
(GWTP)
Références : onBind, onReveal, onReset, onHide,
onUnbind, prepareFromRequest
Partie 3 GWTP
Exemple: Aperçu des méthodes du lifecycle
public class EditManufacturerPresenter
extends PresenterWidget<MyView> {
interface MyView extends View {
}
@Inject
EditManufacturerPresenter(
EventBus eventBus,
MyView view) {
super(eventBus, view);
}
@Override
protected void onBind() {}
@Override
protected void onReveal() {}
@Override
protected void onReset() {}
@Override
protected void onHide() {}
@Override
protected void onUnbind() {}
}
➢ Gère la navigation entre
les places. Beaucoup de code
à écrire autrement.
➢ Offre plusieurs façons de
présenter le token : le hash
dans l’URI, après le #.
➢ Possibilité de rediriger vers une
même page lorsqu’un token
inconnu est accédé. Page 404 à-
la GWTP.
Références : PlaceManager, PlaceRequest, TokenFormatter
Navigation
(GWTP)
Partie 3 GWTP
Exemple: Utiliser le Place Manager
Révéler un name token
private final PlaceManager placeManager;
@Inject
EditManufacturerPresenter(
EventBus eventBus,
MyView view,
PlaceManager placeManager) {
super(eventBus, view);
this.placeManager = placeManager;
}
@Override
public void cancel() {
PlaceRequest placeRequest = new PlaceRequest.Builder()
.nameToken(NameTokens.MANUFACTURERS)
.build();
placeManager.revealPlace(placeRequest);
}
Récupérer le name token courant
private final PlaceManager placeManager;
@Inject
HeaderPresenter(
EventBus eventBus,
MyView view,
PlaceManager placeManager) {
super(eventBus, view);
this.placeManager = placeManager;
}
@Override
protected void onReset() {
PlaceRequest currentPlace = placeManager.getCurrentPlaceRequest();
String currentNameToken = currentPlace.getNameToken();
getView().setActiveMenuItem(currentNameToken);
}
➢ Permet de configurer et restreindre l’
accès à certains tokens.
➢ Possibilité de configurer un gatekeeper
par défaut.
➢ Permet de rediriger vers une même page
lorsque qu’un accès non autorisé est
détecté. Page 401 à-la GWTP.
➢ Attention: Si la page protégée par un
gatekeeper accède à des ressources
serveur, le serveur doit lui aussi protéger
ses ressources!
Références : Gatekeeper, GatekeeperWithParams, @UseGatekeeper,
@GatekeeperParams, @NoGatekeeper, @DefaultGatekeeper
Sécurité
(GWTP)
Partie 3 GWTP
Exemple: Utiliser un Gatekeeper
Gatekeeper
public abstract class AclGatekeeper implements GatekeeperWithParams {
private final CurrentSession currentSession;
private Set<String> permissions;
@Inject
AclGatekeeper(CurrentSession currentSession) {
this.currentSession = currentSession;
this.permissions = new HashSet<>();
}
@Override
public GatekeeperWithParams withParams(String[] params) {
permissions = new HashSet<>(Arrays.asList(params));
return this;
}
@Override
public boolean canReveal() {
return currentSession.isLoggedIn()
&& currentSession.hasPermissions(permissions);
}
}
Utilisation du Gatekeeper
public class EditGroupMembersPresenter
extends Presenter<MyView, MyProxy> {
interface MyView extends View {
}
@ProxyStandard
@NameToken(NameTokens.EDIT_GROUP_MEMBERS)
@UseGatekeeper(AclGatekeeper.class)
@GatekeeperParams({"GROUPS_UPDATE", "MEMBERS_UPDATE"})
interface MyProxy extends ProxyPlace<EditGroupMembersPresenter> {
}
/* snip */
}
➢ Le coeur des fonctions de GWTP :
○ Accès au lifecycle,
○ Gestion centralisée des événements
○ Possède une vue,
○ Communication vue vers presenter via
un UiHandlers.
➢ Permet de développer
des composants réutilisables
et testables.
➢ Permet de définir des Slots
pour imbriquer d’autres PresenterWidgets.
Références : PresenterWidget, UiHandlers, View, OrderedSlot, SingleSlot, Slot
Presenter
Widgets
Partie 3 GWTP
Exemple: Un PresenterWidget
Le PresenterWidget
public class DashboardPresenter extends PresenterWidget<MyView> {
interface MyView extends View {
}
static final Slot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>();
private final Presenter<?, ?> presenter;
@Inject
DashboardPresenter(
EventBus eventBus,
MyView view,
Presenter<?, ?> presenter) {
super(eventBus, view);
this.presenter = presenter;
}
@Override
protected void onBind() {
setInSlot(SLOT_TEMPLATE, presenter);
}
}
Sa vue
public class DashboardView extends ViewImpl implements MyView {
interface Binder extends UiBinder<Widget, DashboardView> {
}
@UiField
SimplePanel template;
@Inject
DashboardView(Binder uiBinder) {
initWidget(uiBinder.createAndBindUi(this));
bindSlot(DashboardPresenter.SLOT_TEMPLATE, template);
}
}
Sa configuration GIN
public class DashboardModule extends AbstractPresenterModule {
@Override
protected void configure() {
bindPresenterWidget(DashboardPresenter.class,
DashboardPresenter.MyView.class, DashboardView.class);
}
}
➢ Un PresenterWidget spécialisé.
➢ Doit être associé à un proxy.
➢ Initialisation uniquement lorsque
nécessaire: améliore le chargement initial.
➢ Peut être associé à un name token (place).
➢ Permet de définir des NestedSlots
pour imbriquer d’autres Presenters
sans couplage.
Références : Presenter, NestedSlot, Proxy, ProxyPlace, @ProxyStandard,
@NameToken
Presenter
Partie 3 GWTP
Exemple: Un Presenter
Le PresenterWidget
public class DashboardPresenter extends Presenter<MyView, MyProxy> {
interface MyView extends View {
}
@ProxyStandard
@NameToken(NameTokens.DASHBOARD)
@UseGatekeeper(LoggedInGatekeeper.class)
interface MyProxy extends ProxyPlace<DashboardPresenter> {
}
static final NestedSlot SLOT_TEMPLATE = new NestedSlot();
@Inject
DashboardPresenter(
EventBus eventBus,
MyView view,
MyProxy proxy) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN);
}
}
public class DashboardView extends ViewImpl implements MyView {
interface Binder extends UiBinder<Widget, DashboardView> {
}
@UiField
SimplePanel template;
@Inject
DashboardView(Binder uiBinder) {
initWidget(uiBinder.createAndBindUi(this));
bindSlot(DashboardPresenter.SLOT_TEMPLATE, template);
}
}
Sa configuration GIN
public class DashboardModule extends AbstractPresenterModule {
@Override
protected void configure() {
bindPresenter(DashboardPresenter.class,
DashboardPresenter.MyView.class, DashboardView.class,
DashboardPresenter.MyProxy.class);
}
}
Sa vue
➢ Un PresenterWidget ayant un popup
comme vue.
➢ Les mêmes avantages qu’un
PresenterWidget: slots, lifecycle.
➢ Délègue l’affichage / masquage du popup
au presenter.
➢ Abstrait le concept de positionnement
du popup.
Références : PopupView, PopupViewImpl, addToPopupSlot, PopupPositioner
Popups
Partie 3 GWTP
Comunication Serveur
Rest-Dispatch
Partie 4
Partie 4 Rest-Dispatch
Amené à disparaître dans GWT.
Nécessite beaucoup de code pour
un cas simple. Renforcit le
couplage entre client et serveur.
Plus simple de conserver
le code client et serveur
compatible.
RPC REST
Permet de communiquer avec n’
importe quel API REST.
Presque tous les serveurs
permettent de faire du REST :
à la mode. Plus facile de modifier l’
API et oublier de mettre le code
client à jour.
➢ Implémentation partielle
de JSR-311 1.1 (JAX-RS).
➢ Définition d’interface et
configuration de l’API
au moyen d’annotations.
➢ Code client (GWT) seulement.
L’API doit être codé en utilisant
une autre technologie.
➢ Possibilité de créer ses propres
extensions.
Rest-Dispatch
Partie 4 Rest-Dispatch
➢ Usage plus explicite que la syntaxe
de base.
➢ Les interfaces et annotations peuvent
être réutilisé sur l’implémentation
serveur (DRY).
➢ Plus simple de conserver un API privé
compatible à la fois sur le client
et le serveur.
➢ Désavantage: Perte du type safety
lors de la création de callbacks.
Extension :
Delegates
Partie 4 Rest-Dispatch
Exemple: Configurer et utiliser Rest-Dispatch
Le module GWT Les paths
Le module GIN L’interface de ressource
<?xml version='1.0' encoding='UTF-8'?>
<module>
<inherits
name="com.gwtplatform.dispatch.rest.delegates.ResourceDelegate"/>
<inherits name='com.gwtplatform.mvp.MvpWithEntryPoint'/>
<source path="client"/>
</module>
public class ResourcesModule extends AbstractGinModule {
@Override
protected void configure() {
install(new RestDispatchAsyncModule.Builder()
/* Additional configurations */
.build());
}
@Provides
@RestApplicationPath
String applicationPath() {
String baseUrl = GWT.getHostPageBaseURL();
if (baseUrl.endsWith("/"))
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
return baseUrl + API;
}
}
public static class DashboardPaths {
public static final String DASHBOARD_ID = "dashboard-id";
public static final String DASHBOARDS = "/dashboards";
public static final String DASHBOARD = "/{" + DASHBOARD_ID + "}";
}
@Path(DASHBOARDS)
public interface DashboardResource {
@GET
List<Dashboard> getAll();
@GET
@Path(DASHBOARD)
Dashboard get(@PathParam(DASHBOARD_ID) int id);
@PUT
@Path(DASHBOARD)
void update(@PathParam(DASHBOARD_ID) int id, Dashboard dashboard);
}
Exemple: Configurer et utiliser Rest-Dispatch (suite)
Utilisation de la ressource
static final SingleSlot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>();
private final ResourceDelegate<DashboardResource> dashboards;
private final DashboardTemplateFactory templateSelector;
@Inject
DashboardPresenter(
EventBus eventBus,
MyView view,
MyProxy proxy,
ResourceDelegate<DashboardResource> dashboards,
DashboardTemplateFactory templateFactory) {
super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN);
this.dashboards = dashboardService;
this.templateFactory = templateFactory;
}
@Override
protected void onReveal() {
dashboards.withCallback(new AbstractAsyncCallback<Dashboard>() {
@Override
public void onSuccess(Dashboard result) {
PresenterWidget<?> template = templateFactory.createTemplate(result);
setInSlot(SLOT_TEMPLATE, template);
}
}).get(DashboardSettings.DEFAULT_DASHBOARD_ID);
}
Tests unitaires
Tester GWTP et Rest-Dispatch
avec Mockito et Jukito
Partie 5
Partie 5 Tests unitaires
Jukito
Jukito permet à vos tests d’utiliser l’
injection de dépendances. Peu
importe le type de test — unitaire,
intégration ou quoi que ce soit de
loufoque —, le code redondant dû
au mocking diminuera grandement.
Rapidement, vous ne pourrez plus
vous passez de sa syntaxe sécuritaire
et basée sur les annotations!
➢ Mocking automatique
➢ Injection des mocks
➢ Annotation @ALL
➢ Léger
➢ S'adapte à toutes tailles de test
➢ Facile d’installation
Fonctionalités
Partie 5 Tests unitaires
Exemples
Sur GitHub
Mockito et Jukito:
ManufacturerDetailPresenter: https://goo.
gl/27NsKL
ManufacturerDetailPresenterTest: https://goo.
gl/irq5rV
ManufacturerDetailPresenterMockitoTest: https:
//goo.gl/EIlj47
Jukito @All:
PersonRenderer: https://goo.gl/o4UGZn
PersonRendererTest: https://goo.gl/uhcL2k
Partie 5 Tests unitaires
Mockito : Initialisation
CAS DE TEST TEST
@Inject
ManufacturerDetailPresenter(
EventBus eventBus,
MyView view,
MyProxy proxy,
ResourceDelegate<ManufacturersResource> manufacturers,
PlaceManager placeManager,
EditManufacturerMessages messages) {
super(eventBus, view, proxy, SLOT_MAIN_CONTENT);
this.manufacturers = manufacturers;
this.placeManager = placeManager;
this.messages = messages;
getView().setUiHandlers(this);
}
public class ManufacturerDetailPresenterMockitoTest {
// SUT
private ManufacturerDetailPresenter presenter;
// Mocks (created by Mockito)
@Mock private EventBus eventBus;
@Mock private MyView view;
@Mock private MyProxy proxy;
@Mock private ResourceDelegate<ManufacturersResource> manufacturers;
@Mock private PlaceManager placeManager;
@Before
public void setUp() {
// Create @Mock and @Captor fields.
MockitoAnnotations.initMocks(this);
// Manual way to create a mock
EditManufacturerMessages messages
= mock(EditManufacturerMessages.class);
// Manual creation of the SUT.
presenter = new ManufacturerDetailPresenter(eventBus, view, proxy,
manufacturers, placeManager, messages);
}
Mockito : Test
CAS DE TEST TEST
@Override
protected void onReveal() {
List<ActionType> actions;
if (createNew) {
actions = Arrays.asList(ActionType.DONE);
} else {
actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE);
}
ChangeActionBarEvent.fire(this, actions, false);
}
@Test
public void onReveal() {
// when
presenter.onReveal();
// then
// Manual way to create an argument captor
ArgumentCaptor<ChangeActionBarEvent> captor
= ArgumentCaptor.forClass(ChangeActionBarEvent.class);
verify(eventBus)
.fireEventFromSource(captor.capture(), same(presenter));
ChangeActionBarEvent event = captor.getValue();
assertThat(event).isNotNull();
assertThat(event.getActions()).containsOnly(ActionType.DONE);
assertThat(event.getTabsVisible()).isFalse();
}
Jukito : Initialisation
CAS DE TEST TEST
@RunWith(JukitoRunner.class)
public class ManufacturerDetailPresenterTest {
// SUT (Injected by Jukito). We don't need to create all dependencies
explicitly, Jukito will mock them.
@Inject
private ManufacturerDetailPresenter presenter;
// Mocks (injected by Jukito)
@Inject
private EventBus eventBus;
// Captors (Create by Mockito)
@Captor
private ArgumentCaptor<ChangeActionBarEvent> changeActionBarEventCaptor;
@Before
public void setUp() {
// Create @Mock and @Captor fields.
MockitoAnnotations.initMocks(this);
}
@Inject
ManufacturerDetailPresenter(
EventBus eventBus,
MyView view,
MyProxy proxy,
ResourceDelegate<ManufacturersResource> manufacturers,
PlaceManager placeManager,
EditManufacturerMessages messages) {
super(eventBus, view, proxy, SLOT_MAIN_CONTENT);
this.manufacturers = manufacturers;
this.placeManager = placeManager;
this.messages = messages;
getView().setUiHandlers(this);
}
Jukito : Test
CAS DE TEST TEST
@Override
protected void onReveal() {
List<ActionType> actions;
if (createNew) {
actions = Arrays.asList(ActionType.DONE);
} else {
actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE);
}
ChangeActionBarEvent.fire(this, actions, false);
}
@Test
public void onReveal_newManufacturer_preparesActionBar() {
// when
presenter.onReveal();
// then
verify(eventBus)
.fireEventFromSource(changeActionBarEventCaptor.capture(),
same(presenter));
ChangeActionBarEvent event = changeActionBarEventCaptor.getValue();
assertThat(event).isNotNull();
assertThat(event.getActions()).containsOnly(ActionType.DONE);
assertThat(event.getTabsVisible()).isFalse();
}
Jukito : Utiliser @All pour tester plusieurs cas
CAS DE TEST TEST
@Inject
PersonRenderer(
Messages messages,
@Assisted Mode mode) {
this.mode = mode;
this.messages = messages;
}
public String render(Person person) {
String result = "";
if (person != null) {
String firstName = person.getFirstName();
String middleName = person.getMiddleName();
String lastName = person.getLastName();
String title = renderTitle(person);
if (mode.isDisplayTitle() && !Strings.isNullOrEmpty(title)) {
result += title + " ";
}
if (mode.isDisplayFirstName() && !Strings.isNullOrEmpty(firstName)) {
result += firstName + " ";
}
if (mode.isDisplayMiddleName() && !Strings.isNullOrEmpty(middleName)) {
result += middleName + " ";
}
if (mode.isDisplayLastName() && !Strings.isNullOrEmpty(lastName)) {
result += lastName;
}
}
if (result.isEmpty()) {
result = messages.unknown();
}
return result;
}
public class PersonNameTestCase {
private final Person person;
private Mode mode;
private String expected;
public PersonNameTestCase() {
person = null;
}
public PersonNameTestCase(
String firstName,
String lastName) {
person = new Person(firstName, lastName);
}
/* Setters */
public Person getPerson() {
return person;
}
public Mode getMode() {
return mode;
}
public String getExpected() {
return expected;
}
}
Jukito : Utiliser @All pour tester plusieurs cas (suite)
TEST TEST
@RunWith(JukitoRunner.class)
public class PersonRendererTest {
public static class Module extends JukitoModule {
@Override
protected void configureTest() {
bindManyInstances(PersonNameTestCase.class,
new PersonNameTestCase("Zom", "Bee")
.mode(Mode.SHORT).expected("Zom Bee"),
new PersonNameTestCase("Zom", "Bee").title(Title.MR)
.mode(Mode.SHORT).expected("Zom Bee"),
new PersonNameTestCase("Zom", "Bee").middleName("Buzz")
.mode(Mode.SHORT).expected("Zom Bee"),
new PersonNameTestCase("Zom", "Bee").title(Title.MR).middleName("Buzz")
.mode(Mode.SHORT).expected("Zom Bee"),
new PersonNameTestCase("Zom", "Bee")
.mode(Mode.FORMAL).expected("Zom Bee"),
new PersonNameTestCase("Zom", "Bee").title(Title.MS)
.mode(Mode.FORMAL).expected("Ms. Zom Bee"),
new PersonNameTestCase("Zom", "Bee").middleName("Buzz")
.mode(Mode.FORMAL).expected("Zom Buzz Bee"),
new PersonNameTestCase("Zom", "Bee").title(Title.MS).middleName("Buzz")
.mode(Mode.FORMAL).expected("Ms. Zom Buzz Bee"),
new PersonNameTestCase()
.mode(Mode.SHORT).expected(UNKNOWN),
new PersonNameTestCase()
.mode(Mode.FORMAL).expected(UNKNOWN)
);
}
}
private static final String UNKNOWN = "unknown";
@Inject
private Messages messages;
@Before
public void setUp() {
given(messages.unknown()).willReturn(UNKNOWN);
given(messages.title(Title.MR)).willReturn("Mr.");
given(messages.title(Title.MS)).willReturn("Ms.");
}
@Test
public void render(@All PersonNameTestCase testCase) {
// given
Person person = testCase.getPerson();
Mode mode = testCase.getMode();
PersonRenderer renderer = new PersonRenderer(messages, mode);
// when
String result = renderer.render(person);
// then
String expected = testCase.getExpected();
assertThat(result).overridingErrorMessage(
"Expected %s with mode %s to be '%s'.", person, mode, expected)
.isEqualTo(expected);
}
Jukito : Utilitaires pour tester Rest-Dispatch
CAS DE TEST TEST
public void onSave(ManufacturerDto manufacturer) {
manufacturersDelegate
.withCallback(new AbstractAsyncCallback<ManufacturerDto>(this) {
@Override
public void onSuccess(ManufacturerDto newManufacturer) {
DisplayMessageEvent.fire(ManufacturerDetailPresenter.this,
new Message(messages.manufacturerSaved(), SUCCESS));
PlaceRequest placeRequest = new Builder()
.nameToken(NameTokens.MANUFACTURER)
.build();
placeManager.revealPlace(placeRequest);
}
})
.saveOrCreate(manufacturerDto);
}
@Test
public void onSave_showsMessage_revealsManufacturers(
EditManufacturerMessages messages) {
// given
ManufacturerDto manufacturer = new ManufacturerDto();
ManufacturerDto resultManufacturer = new ManufacturerDto();
givenDelegate(manufacturersResource)
.succeed().withResult(resultManufacturer)
.when().saveOrCreate(same(manufacturer));
given(messages.manufacturerSaved()).willReturn(A_MESSAGE);
// when
presenter.onSave(manufacturer);
// then
// note `isA` is used instead of `any`. This is because the event bus
// accepts all `GwtEvent` subclasses. `isA` also verifies the type
verify(eventBus)
.fireEventFromSource(isA(DisplayMessageEvent.class),same(presenter));
verify(placeManager).revealPlace(placeRequestCaptor.capture());
PlaceRequest placeRequest = placeRequestCaptor.getValue();
assertThat(placeRequest.getNameToken())
.isEqualTo(NameTokens.MANUFACTURER);
}
GWT
Meilleures Pratiques
Partie 6
Avoid widgets
as much as you can
BEST PRACTICE 1
Partie 6
WIDGETS
ARE HEAVY.
BECAUSE
Partie 6
TAKE CONTROL
OF YOUR HTML!
Full Event
Mechanism
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Even if you don’t need it
Full Event
Mechanism
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
How to attach
event handler
to elements ?
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Use
widget
To encapsulate complex
component to reuse
» prefer PresenterWidget if the component
has a lot of business logic
» In the futur: Web Component.
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Cell widgets (CellTable, CellList…)
HtmlPanel
Exceptions
BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN
Partie 6
Use an
Event Bus
BEST PRACTICE 2
Partie 6
Use Dependency
Injection
BEST PRACTICE 3
Partie 6
Use
MVP Pattern
BEST PRACTICE 4
Partie 6
Make
Navigation Easy
BEST PRACTICE 5
Partie 6
Use directly
your name tokens
BP 5 : MAKE NAVIGATION EASY
Partie 6
Use CSS
as much as you can
BEST PRACTICE 6
Partie 6
Loas everything
you can in the html page
BEST PRACTICE 7
Partie 6
UNIT
TESTS
Partie 6
Test each
protected and public
methods of your presenters.
UNIT TESTS
Partie 6
CODE
REVIEW
Partie 6
Improve quality
of your code
CODE REVIEW
Partie 6
Detect Bugs
earlier
CODE REVIEW
Partie 6
A way to learn
from your peers
CODE REVIEW
Partie 6
Web based
code review system
CODE REVIEW
Partie 6
CODE REVIEW
Best practices
Partie 6
TAKE CONTROL
OF YOUR HTML!USE A STYLE GUIDE !
Address/fix
all comments
before you merge.
CODE REVIEW Best Practice
Partie 6
CONTINUOUS
INTEGRATION
Partie 6
Partie 6
The first to do code review
is your CI Server
CONTINUOUS INTEGRATION
Partie 6
Partie 6
Enforce
Check Style
CONTINUOUS INTEGRATION
Partie 6
ACTORS
Partie 6
Partie 6
Partie 6
Designer
Partie 6
Web Integrators
Partie 6
Developpers
Partie 6
Partie 6
Partie 6
Partie 6
Partie 6
SUCCESS
Partie 6
GWT 3.0
Partie 7
Java 8 Support
Partie 7
JSInterop
Partie 7
Export
any Java code
to Javascript
JSinterop
Partie 7
Export any
Javascript API
to Java
Partie 7
JSinterop
Subclassing
JavaScript
Objects
Partie 7
JSinterop
Web component
Material design
in GWT
Partie 7
JSinterop
Start to
experiment
with GWT 2.7
» Turn it on with a flag
-XjsInteropMode
» Subject to change !
Partie 7
JSinterop
MERCI
QUESTIONS ?

Contenu connexe

Tendances

Bbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchBbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchIdriss Neumann
 
20081008 - Tours Jug - Apache Maven
20081008  - Tours Jug - Apache Maven20081008  - Tours Jug - Apache Maven
20081008 - Tours Jug - Apache MavenArnaud Héritier
 
Gwt fast overview_v1
Gwt fast overview_v1Gwt fast overview_v1
Gwt fast overview_v1David Herviou
 
Présentation du retour d'expérience sur Git
Présentation du retour d'expérience sur GitPrésentation du retour d'expérience sur Git
Présentation du retour d'expérience sur GitIppon
 
JCertif 2012 : Maven par la pratique
JCertif 2012 : Maven par la pratiqueJCertif 2012 : Maven par la pratique
JCertif 2012 : Maven par la pratiqueRossi Oddet
 
20091020 - Normandy Jug - Builders Battle
20091020 - Normandy Jug - Builders Battle20091020 - Normandy Jug - Builders Battle
20091020 - Normandy Jug - Builders BattleArnaud Héritier
 
Cours du soir_gwt
Cours du soir_gwtCours du soir_gwt
Cours du soir_gwtSaid Talibi
 
Une (simple) présentation de Apache Maven 2
Une (simple) présentation de Apache Maven 2Une (simple) présentation de Apache Maven 2
Une (simple) présentation de Apache Maven 2teejug
 
Présentation Maven
Présentation MavenPrésentation Maven
Présentation MavenSOAT
 
Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Eric SIBER
 
Introduction à GWT - GTI780 & MTI780 - ETS - A09
Introduction à GWT - GTI780 & MTI780 - ETS - A09Introduction à GWT - GTI780 & MTI780 - ETS - A09
Introduction à GWT - GTI780 & MTI780 - ETS - A09Claude Coulombe
 
Introduction à GWT - GTI780 & MTI780 - ETS - A08
Introduction à GWT - GTI780 & MTI780 - ETS - A08Introduction à GWT - GTI780 & MTI780 - ETS - A08
Introduction à GWT - GTI780 & MTI780 - ETS - A08Claude Coulombe
 
À La découverte de flow3 - t3con12
À La découverte de flow3 -  t3con12À La découverte de flow3 -  t3con12
À La découverte de flow3 - t3con12mguermazi
 
Quand java prend de la vitesse, apache maven vous garde sur les rails
Quand java prend de la vitesse, apache maven vous garde sur les railsQuand java prend de la vitesse, apache maven vous garde sur les rails
Quand java prend de la vitesse, apache maven vous garde sur les railsArnaud Héritier
 

Tendances (20)

Formation gwt
Formation gwtFormation gwt
Formation gwt
 
Bbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchBbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic search
 
20081008 - Tours Jug - Apache Maven
20081008  - Tours Jug - Apache Maven20081008  - Tours Jug - Apache Maven
20081008 - Tours Jug - Apache Maven
 
Sonar-Hodson-Maven
Sonar-Hodson-MavenSonar-Hodson-Maven
Sonar-Hodson-Maven
 
Gwt fast overview_v1
Gwt fast overview_v1Gwt fast overview_v1
Gwt fast overview_v1
 
Présentation du retour d'expérience sur Git
Présentation du retour d'expérience sur GitPrésentation du retour d'expérience sur Git
Présentation du retour d'expérience sur Git
 
JCertif 2012 : Maven par la pratique
JCertif 2012 : Maven par la pratiqueJCertif 2012 : Maven par la pratique
JCertif 2012 : Maven par la pratique
 
20091020 - Normandy Jug - Builders Battle
20091020 - Normandy Jug - Builders Battle20091020 - Normandy Jug - Builders Battle
20091020 - Normandy Jug - Builders Battle
 
Cours du soir_gwt
Cours du soir_gwtCours du soir_gwt
Cours du soir_gwt
 
Une (simple) présentation de Apache Maven 2
Une (simple) présentation de Apache Maven 2Une (simple) présentation de Apache Maven 2
Une (simple) présentation de Apache Maven 2
 
Présentation Maven
Présentation MavenPrésentation Maven
Présentation Maven
 
Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)Spring Meetup Paris - Back to the basics of Spring (Boot)
Spring Meetup Paris - Back to the basics of Spring (Boot)
 
Introduction à GWT - GTI780 & MTI780 - ETS - A09
Introduction à GWT - GTI780 & MTI780 - ETS - A09Introduction à GWT - GTI780 & MTI780 - ETS - A09
Introduction à GWT - GTI780 & MTI780 - ETS - A09
 
Gradle_BreizJUG
Gradle_BreizJUGGradle_BreizJUG
Gradle_BreizJUG
 
Apache Maven 3
Apache Maven 3Apache Maven 3
Apache Maven 3
 
Introduction à GWT - GTI780 & MTI780 - ETS - A08
Introduction à GWT - GTI780 & MTI780 - ETS - A08Introduction à GWT - GTI780 & MTI780 - ETS - A08
Introduction à GWT - GTI780 & MTI780 - ETS - A08
 
Maven
MavenMaven
Maven
 
À La découverte de flow3 - t3con12
À La découverte de flow3 -  t3con12À La découverte de flow3 -  t3con12
À La découverte de flow3 - t3con12
 
Présentation1
Présentation1Présentation1
Présentation1
 
Quand java prend de la vitesse, apache maven vous garde sur les rails
Quand java prend de la vitesse, apache maven vous garde sur les railsQuand java prend de la vitesse, apache maven vous garde sur les rails
Quand java prend de la vitesse, apache maven vous garde sur les rails
 

En vedette

Buzz Digital O.R. White Paper
Buzz Digital O.R. White PaperBuzz Digital O.R. White Paper
Buzz Digital O.R. White PaperBrainlab
 
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...Codemotion
 
Welcome to the digital era
Welcome to the digital eraWelcome to the digital era
Welcome to the digital eraJasper Meurs
 
Xamarin day3 - Android Fragments
Xamarin day3 - Android FragmentsXamarin day3 - Android Fragments
Xamarin day3 - Android FragmentsSubodh Pushpak
 
Consultation document en
Consultation document enConsultation document en
Consultation document enHonza Beranek
 
EDYS: Project Superstars
EDYS: Project SuperstarsEDYS: Project Superstars
EDYS: Project SuperstarsGabriel BREZOIU
 
Opinión personal sobre la elavoración del blog
Opinión personal sobre la elavoración del blogOpinión personal sobre la elavoración del blog
Opinión personal sobre la elavoración del bloglaycar
 
водорості
водоростіводорості
водоростіkatia0401
 
Grow Foundation for Ghana
Grow Foundation for GhanaGrow Foundation for Ghana
Grow Foundation for GhanaElyse Schaefer
 
Procesamiento criminalístico
Procesamiento criminalísticoProcesamiento criminalístico
Procesamiento criminalísticoWael Hikal
 

En vedette (20)

Optimistasiempre
OptimistasiempreOptimistasiempre
Optimistasiempre
 
Buzz Digital O.R. White Paper
Buzz Digital O.R. White PaperBuzz Digital O.R. White Paper
Buzz Digital O.R. White Paper
 
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...
Tecnologie e Startup: ICT è solo una commodity? - Matteo Valoriani - Codemoti...
 
Bouldrey cv 2015
Bouldrey cv 2015Bouldrey cv 2015
Bouldrey cv 2015
 
Welcome to the digital era
Welcome to the digital eraWelcome to the digital era
Welcome to the digital era
 
Xamarin day3 - Android Fragments
Xamarin day3 - Android FragmentsXamarin day3 - Android Fragments
Xamarin day3 - Android Fragments
 
Investigacion dirigida
Investigacion dirigidaInvestigacion dirigida
Investigacion dirigida
 
Taller sistemas de gestión de la innovación
Taller sistemas de gestión de la innovaciónTaller sistemas de gestión de la innovación
Taller sistemas de gestión de la innovación
 
Folleto padres
Folleto padresFolleto padres
Folleto padres
 
Consultation document en
Consultation document enConsultation document en
Consultation document en
 
EDYS: Project Superstars
EDYS: Project SuperstarsEDYS: Project Superstars
EDYS: Project Superstars
 
2010-02-28 FNF PAK - German Development Initiatives In Pakistan
2010-02-28 FNF PAK - German Development Initiatives In Pakistan2010-02-28 FNF PAK - German Development Initiatives In Pakistan
2010-02-28 FNF PAK - German Development Initiatives In Pakistan
 
Planificación de clases 2013
Planificación de clases 2013Planificación de clases 2013
Planificación de clases 2013
 
Opinión personal sobre la elavoración del blog
Opinión personal sobre la elavoración del blogOpinión personal sobre la elavoración del blog
Opinión personal sobre la elavoración del blog
 
Web quest
Web questWeb quest
Web quest
 
DC_Writing Sample
DC_Writing SampleDC_Writing Sample
DC_Writing Sample
 
водорості
водоростіводорості
водорості
 
2009-08-15 FNF and its Partners
2009-08-15 FNF and its Partners2009-08-15 FNF and its Partners
2009-08-15 FNF and its Partners
 
Grow Foundation for Ghana
Grow Foundation for GhanaGrow Foundation for Ghana
Grow Foundation for Ghana
 
Procesamiento criminalístico
Procesamiento criminalísticoProcesamiento criminalístico
Procesamiento criminalístico
 

Similaire à Uni.sherbrooke 2015 créez la meilleur application grâce à gwt, gwtp et jukito

Université de la performance - Devoxx France
Université de la performance - Devoxx FranceUniversité de la performance - Devoxx France
Université de la performance - Devoxx FranceMarc Bojoly
 
ASP.NET MVC 6
ASP.NET MVC 6ASP.NET MVC 6
ASP.NET MVC 6Microsoft
 
Chaine de production pipeline
Chaine de production   pipelineChaine de production   pipeline
Chaine de production pipelineNicolas wallerand
 
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...ENSIBS
 
AFUP Aix/Marseille - 16 mai 2017 - Open API
AFUP Aix/Marseille - 16 mai 2017 - Open APIAFUP Aix/Marseille - 16 mai 2017 - Open API
AFUP Aix/Marseille - 16 mai 2017 - Open APIRomain Cambien
 
Java entreprise edition et industrialisation du génie logiciel par m.youssfi
Java entreprise edition et industrialisation du génie logiciel par m.youssfiJava entreprise edition et industrialisation du génie logiciel par m.youssfi
Java entreprise edition et industrialisation du génie logiciel par m.youssfiENSET, Université Hassan II Casablanca
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsJulien Wittouck
 
20081023 - Paris Vi Master STL TA - Initiation Maven
20081023 - Paris Vi Master STL TA - Initiation Maven20081023 - Paris Vi Master STL TA - Initiation Maven
20081023 - Paris Vi Master STL TA - Initiation MavenArnaud Héritier
 
Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?Antoine Rey
 
Les générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidienLes générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidienNicolas Carlo
 
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...MSDEVMTL
 
NightClazz Build Tools & Continuous Delivery Avancé
NightClazz Build Tools & Continuous Delivery AvancéNightClazz Build Tools & Continuous Delivery Avancé
NightClazz Build Tools & Continuous Delivery AvancéZenika
 
Morning tech #2 - Démarche performance slides
Morning tech #2 - Démarche performance slidesMorning tech #2 - Démarche performance slides
Morning tech #2 - Démarche performance slidesOxalide
 
Oxalide Morning tech #2 - démarche performance
Oxalide Morning tech #2 - démarche performanceOxalide Morning tech #2 - démarche performance
Oxalide Morning tech #2 - démarche performanceLudovic Piot
 
Kubernetes Meetup Paris #5 - Metriques applicatives k8s
Kubernetes Meetup Paris #5 - Metriques applicatives k8sKubernetes Meetup Paris #5 - Metriques applicatives k8s
Kubernetes Meetup Paris #5 - Metriques applicatives k8sArnaud MAZIN
 
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDKGreenIvory
 
TechDays 2010 (CLO202) : Introduction à Windows Azure
TechDays 2010 (CLO202) : Introduction à Windows AzureTechDays 2010 (CLO202) : Introduction à Windows Azure
TechDays 2010 (CLO202) : Introduction à Windows AzureThomas Conté
 
Presentation du socle technique Java open source Scub Foundation
Presentation du socle technique Java open source Scub FoundationPresentation du socle technique Java open source Scub Foundation
Presentation du socle technique Java open source Scub FoundationStéphane Traumat
 

Similaire à Uni.sherbrooke 2015 créez la meilleur application grâce à gwt, gwtp et jukito (20)

Perf university
Perf universityPerf university
Perf university
 
Université de la performance - Devoxx France
Université de la performance - Devoxx FranceUniversité de la performance - Devoxx France
Université de la performance - Devoxx France
 
ASP.NET MVC 6
ASP.NET MVC 6ASP.NET MVC 6
ASP.NET MVC 6
 
Chaine de production pipeline
Chaine de production   pipelineChaine de production   pipeline
Chaine de production pipeline
 
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...
Agile Tour Paris 2014 : Ma stack d'outils Agiles, tout un programme !, Cedric...
 
AFUP Aix/Marseille - 16 mai 2017 - Open API
AFUP Aix/Marseille - 16 mai 2017 - Open APIAFUP Aix/Marseille - 16 mai 2017 - Open API
AFUP Aix/Marseille - 16 mai 2017 - Open API
 
Java entreprise edition et industrialisation du génie logiciel par m.youssfi
Java entreprise edition et industrialisation du génie logiciel par m.youssfiJava entreprise edition et industrialisation du génie logiciel par m.youssfi
Java entreprise edition et industrialisation du génie logiciel par m.youssfi
 
Spring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'tsSpring Boot & Containers - Do's & Don'ts
Spring Boot & Containers - Do's & Don'ts
 
20081023 - Paris Vi Master STL TA - Initiation Maven
20081023 - Paris Vi Master STL TA - Initiation Maven20081023 - Paris Vi Master STL TA - Initiation Maven
20081023 - Paris Vi Master STL TA - Initiation Maven
 
Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?Quoi de neuf à Devoxx France 2017 ?
Quoi de neuf à Devoxx France 2017 ?
 
Les générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidienLes générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidien
 
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...
Sébastien Coutu: Copy this Meetup Devops - microservices - infrastructure imm...
 
[Tuto] Big datatrack : Web Tracker
[Tuto] Big datatrack : Web Tracker[Tuto] Big datatrack : Web Tracker
[Tuto] Big datatrack : Web Tracker
 
NightClazz Build Tools & Continuous Delivery Avancé
NightClazz Build Tools & Continuous Delivery AvancéNightClazz Build Tools & Continuous Delivery Avancé
NightClazz Build Tools & Continuous Delivery Avancé
 
Morning tech #2 - Démarche performance slides
Morning tech #2 - Démarche performance slidesMorning tech #2 - Démarche performance slides
Morning tech #2 - Démarche performance slides
 
Oxalide Morning tech #2 - démarche performance
Oxalide Morning tech #2 - démarche performanceOxalide Morning tech #2 - démarche performance
Oxalide Morning tech #2 - démarche performance
 
Kubernetes Meetup Paris #5 - Metriques applicatives k8s
Kubernetes Meetup Paris #5 - Metriques applicatives k8sKubernetes Meetup Paris #5 - Metriques applicatives k8s
Kubernetes Meetup Paris #5 - Metriques applicatives k8s
 
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK
20091006 Dev Forum Fr 001Introduction à GWT en utilisant AppCase SDK
 
TechDays 2010 (CLO202) : Introduction à Windows Azure
TechDays 2010 (CLO202) : Introduction à Windows AzureTechDays 2010 (CLO202) : Introduction à Windows Azure
TechDays 2010 (CLO202) : Introduction à Windows Azure
 
Presentation du socle technique Java open source Scub Foundation
Presentation du socle technique Java open source Scub FoundationPresentation du socle technique Java open source Scub Foundation
Presentation du socle technique Java open source Scub Foundation
 

Plus de Arcbees

La Marque est Morte, Vive l'intelligence artificielle.
La Marque est Morte, Vive l'intelligence artificielle.La Marque est Morte, Vive l'intelligence artificielle.
La Marque est Morte, Vive l'intelligence artificielle.Arcbees
 
GWTcon 2015 - brad, Brand and Brands
GWTcon 2015 - brad, Brand and BrandsGWTcon 2015 - brad, Brand and Brands
GWTcon 2015 - brad, Brand and BrandsArcbees
 
GWTcon 2015 - Best development practices for GWT web applications
GWTcon 2015 - Best development practices for GWT web applications GWTcon 2015 - Best development practices for GWT web applications
GWTcon 2015 - Best development practices for GWT web applications Arcbees
 
Designer chez les geeks - nov 2015
Designer chez les geeks - nov 2015Designer chez les geeks - nov 2015
Designer chez les geeks - nov 2015Arcbees
 
mark, Marque et Marques
mark, Marque et Marquesmark, Marque et Marques
mark, Marque et MarquesArcbees
 
Introduction à GWT
Introduction à GWTIntroduction à GWT
Introduction à GWTArcbees
 
mark, Marque et Marques
mark, Marque et Marquesmark, Marque et Marques
mark, Marque et MarquesArcbees
 
GWT Brand Guidelines 1.1
GWT Brand Guidelines 1.1GWT Brand Guidelines 1.1
GWT Brand Guidelines 1.1Arcbees
 
Css3 and gwt in perfect harmony
Css3 and gwt in perfect harmonyCss3 and gwt in perfect harmony
Css3 and gwt in perfect harmonyArcbees
 

Plus de Arcbees (9)

La Marque est Morte, Vive l'intelligence artificielle.
La Marque est Morte, Vive l'intelligence artificielle.La Marque est Morte, Vive l'intelligence artificielle.
La Marque est Morte, Vive l'intelligence artificielle.
 
GWTcon 2015 - brad, Brand and Brands
GWTcon 2015 - brad, Brand and BrandsGWTcon 2015 - brad, Brand and Brands
GWTcon 2015 - brad, Brand and Brands
 
GWTcon 2015 - Best development practices for GWT web applications
GWTcon 2015 - Best development practices for GWT web applications GWTcon 2015 - Best development practices for GWT web applications
GWTcon 2015 - Best development practices for GWT web applications
 
Designer chez les geeks - nov 2015
Designer chez les geeks - nov 2015Designer chez les geeks - nov 2015
Designer chez les geeks - nov 2015
 
mark, Marque et Marques
mark, Marque et Marquesmark, Marque et Marques
mark, Marque et Marques
 
Introduction à GWT
Introduction à GWTIntroduction à GWT
Introduction à GWT
 
mark, Marque et Marques
mark, Marque et Marquesmark, Marque et Marques
mark, Marque et Marques
 
GWT Brand Guidelines 1.1
GWT Brand Guidelines 1.1GWT Brand Guidelines 1.1
GWT Brand Guidelines 1.1
 
Css3 and gwt in perfect harmony
Css3 and gwt in perfect harmonyCss3 and gwt in perfect harmony
Css3 and gwt in perfect harmony
 

Uni.sherbrooke 2015 créez la meilleur application grâce à gwt, gwtp et jukito

  • 1. MEILLEURES PRATIQUES DE DÉVELOPPEMENT GRÂCE À GWT, GWTP ET JUKITO CHRISTOPHER VIEL CHRISTIAN GOUDREAU Département de Génie Informatique Université de Sherbrooke Juin 2015
  • 3. Partie 1 Présentation d’Arcbees Partie 2 Résumé du MVP Model-View-Presenter Partie 3 Survol de GWTP Framework MVP pour GWT Partie 4 Communication serveur Rest-Dispatch Slides disponibles à https://goo.gl/PBzmwL
  • 4. Partie 5 Tests unitaires Exemples avec Mockito et Jukito Partie 6 Meilleures pratiques Partie 7 GWT 3.0 Slides disponibles à https://goo.gl/PBzmwL
  • 8. Notre mission Créer des produits permettant à nos clients, nos abeilles et la communauté de développeurs d'optimiser leurs processus, habitudes et façons de penser, en créant continuellement de la valeur et en transformant la façon dont ils travaillent. Partie 1 Arcbees
  • 9. Origine de Arcbees Afin d'améliorer leur efficacité de production, les deux cofondateurs ont conçu GWT-Platform, un outil open source de création d'applications web doté d'une incroyable capacité d'adaptation accélérant la vitesse de développement des applications. Partie 1 Arcbees
  • 10.
  • 11. Évolution rapide Rapidement, GWT-Platform a généré une importante demande de soutien de la part de ses utilisateurs et les deux entrepreneurs n'ont eu d'autres choix que de créer officiellement Arcbees le 16 juillet 2010 afin de répondre à ces requêtes. Partie 1 Arcbees
  • 12. Reconnaissance de Google En moins d'un an, Google a remarqué la jeune entreprise qui a été invitée à donner une conférence au Google I/O, puis, en 2012, Christian a été nommé sur le Comité de direction de GWT, groupe en charge de la direction et de l'évolution du framework. Partie 1 Arcbees
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 21. ➢ Model : Définit les données qui seront ultimement gérées dans l’interface utilisateur. ➢ View : Affiche le modèle pour l’utilisateur. Généralement passive: seulement de la logique d’ affichage. Délègue les interactions utilisateur (events) au presenter. ➢ Presenter : Récupère et met à jour le modèle. Interprète le modèle les données pour les transférer à la vue. Partie 2 MVP M V P
  • 22. Caractéristiques du MVP ➢ Couplage faible entre les différentes couches. ➢ Flot très simple, presque linéaire. ➢ Communique exclusivement au moyen d’interfaces. ➢ Vue passive. Ne prend aucune décision et notifie le presenter. Partie 2 MVP
  • 25. Quand l’utiliser ➢ Idéal lorsqu’un interface utilisateur est nécessaire pour contrôler l’application. ➢ Pratique lorsqu’il y a beaucoup d’édition de données (formulaires, CRUD, etc.) ➢ Simple à mettre en place lorsqu’une communication entre client - serveur est requise. Partie 2 MVP Quand l’utiliser
  • 26. Quand l’utiliser ➢ Simple à tester : Le faible couplage permet de bien isoler les couches. ➢ Réutilisation de code plus aisée : Le flot plus simple et l’utilisation intensive d’interfaces permet de mieux extraire le code réutilisable. Partie 2 MVP Les bénéfices et les désavantages
  • 27. Quand l’utiliser ➢ Flexible : Il est facile de remplacer n’importe quel composant tant qu’on respecte le contrat défini par les interfaces. ➢ Peut se combiner à d’autres design patterns. Partie 2 MVP Les bénéfices et les désavantages
  • 28. ➢ Le grand nombre d’interface à gérer augmente la complexité du code. Les IDEs modernes aident néanmoins à atténuer ce problème (détection des implémentations). ➢ La vue passive introduit beaucoup de code boilerplate: beaucoup d’aller-retour entre le presenter et la vue. Partie 2 MVP Les bénéfices et les désavantages
  • 29. Partie 3 Survol de GWTP Framework MVP pour GWT
  • 30. ➢ Repose sur GWT et GIN (GWT INjection). ➢ Abstraire la gestion du pattern MVP, mais aussi imposer son utilisation. ➢ Abstraire la gestion cliente: initialiser l’ application, navigation, communication avec le serveur. ➢ Réduire le code à écrire habituellement requis en GWT. ➢ Optimisations: Proxy, code splitting... GWTP Partie 3 GWTP
  • 31. ➢ Permet un découplage plus poussé et rend le code plus modulaire (DIP, IoC). ○ Aide à atteindre de meilleures pratiques, ○ Classes immutables, ○ Créer de nouvelles dépendances devient plus simple : Extraction de code vers des collaborateurs (SRP). ➢ Tend à rendre les tests plus simples: Jukito, GuiceBerry… ➢ En bref : Aide à atteindre les principes SOLID Injection de dépendances Partie 3 GWTP
  • 32. ➢ Limiter le code redondant à écrire qui est habituellement requis en GWT. ➢ Séparer les phases d’initialisation en phases logiques simple à étendre ou remplacer. ➢ Permet d’utiliser l’injection de dépendances plus tôt dans le programme. ➢ Configuration initiale à un seul endroit. Références : DefaultModule, PreBootstrapper, Bootstrapper Initialisation de l’application Partie 3 GWTP
  • 33. Exemple: Initialisation et Bootstrapper Configuration via le module GWT public class ClientModule extends AbstractGinModule { @Override protected void configure() { install(new DefaultModule.Builder() .defaultPlace(NameTokens.LOGIN) .errorPlace(NameTokens.LOGIN) .unauthorizedPlace(NameTokens.UNAUTHORIZED) .build()); install(new ApplicationModule()); bind(ResourceLoader.class).asEagerSingleton(); } } <?xml version="1.0" encoding="UTF-8"?> <module> <inherits name="com.gwtplatform.mvp.MvpWithEntryPoint"/> <source path="client"/> <source path="shared"/> <set-configuration-property name="gwtp.bootstrapper" value="com.arcbees.demo.client.DefaultBootstrapper"/> <extend-configuration-property name="gin.ginjector.modules" value="com.arcbees.demo.client.ClientModule"/> </module> Bootstrapper (facultatif!) Configuration via GIN public class DefaultBootstrapper implements Bootstrapper { private final PlaceManager placeManager; @Inject DefaultBootstrapper(PlaceManager placeManager) { this.placeManager = placeManager; } @Override public void onBootstrap() { // Initialize states then reveal the app: placeManager.getCurrentPlaceRequest(); } }
  • 34. ➢ Permet de réagir à différents stimuli provenant d’ailleurs dans l’application. ➢ Permet d’éviter de coupler des classes qui n’ont aucun point commun. ➢ Implémentation générique et de l’Observer. Références : addRegisteredHandler, addVisibleHandler, @ProxyEvent Plus de détails: https://blog.arcbees.com/?p=699 Évenements (GWT, EventBus) Partie 3 GWTP
  • 35. ➢ Isoler le comportement du presenter pour chaque phase de sa vie. ➢ Comportements isolés donc plus simple à tester unitairement. Lifecycle (GWTP) Références : onBind, onReveal, onReset, onHide, onUnbind, prepareFromRequest Partie 3 GWTP
  • 36. Exemple: Aperçu des méthodes du lifecycle public class EditManufacturerPresenter extends PresenterWidget<MyView> { interface MyView extends View { } @Inject EditManufacturerPresenter( EventBus eventBus, MyView view) { super(eventBus, view); } @Override protected void onBind() {} @Override protected void onReveal() {} @Override protected void onReset() {} @Override protected void onHide() {} @Override protected void onUnbind() {} }
  • 37. ➢ Gère la navigation entre les places. Beaucoup de code à écrire autrement. ➢ Offre plusieurs façons de présenter le token : le hash dans l’URI, après le #. ➢ Possibilité de rediriger vers une même page lorsqu’un token inconnu est accédé. Page 404 à- la GWTP. Références : PlaceManager, PlaceRequest, TokenFormatter Navigation (GWTP) Partie 3 GWTP
  • 38. Exemple: Utiliser le Place Manager Révéler un name token private final PlaceManager placeManager; @Inject EditManufacturerPresenter( EventBus eventBus, MyView view, PlaceManager placeManager) { super(eventBus, view); this.placeManager = placeManager; } @Override public void cancel() { PlaceRequest placeRequest = new PlaceRequest.Builder() .nameToken(NameTokens.MANUFACTURERS) .build(); placeManager.revealPlace(placeRequest); } Récupérer le name token courant private final PlaceManager placeManager; @Inject HeaderPresenter( EventBus eventBus, MyView view, PlaceManager placeManager) { super(eventBus, view); this.placeManager = placeManager; } @Override protected void onReset() { PlaceRequest currentPlace = placeManager.getCurrentPlaceRequest(); String currentNameToken = currentPlace.getNameToken(); getView().setActiveMenuItem(currentNameToken); }
  • 39. ➢ Permet de configurer et restreindre l’ accès à certains tokens. ➢ Possibilité de configurer un gatekeeper par défaut. ➢ Permet de rediriger vers une même page lorsque qu’un accès non autorisé est détecté. Page 401 à-la GWTP. ➢ Attention: Si la page protégée par un gatekeeper accède à des ressources serveur, le serveur doit lui aussi protéger ses ressources! Références : Gatekeeper, GatekeeperWithParams, @UseGatekeeper, @GatekeeperParams, @NoGatekeeper, @DefaultGatekeeper Sécurité (GWTP) Partie 3 GWTP
  • 40. Exemple: Utiliser un Gatekeeper Gatekeeper public abstract class AclGatekeeper implements GatekeeperWithParams { private final CurrentSession currentSession; private Set<String> permissions; @Inject AclGatekeeper(CurrentSession currentSession) { this.currentSession = currentSession; this.permissions = new HashSet<>(); } @Override public GatekeeperWithParams withParams(String[] params) { permissions = new HashSet<>(Arrays.asList(params)); return this; } @Override public boolean canReveal() { return currentSession.isLoggedIn() && currentSession.hasPermissions(permissions); } } Utilisation du Gatekeeper public class EditGroupMembersPresenter extends Presenter<MyView, MyProxy> { interface MyView extends View { } @ProxyStandard @NameToken(NameTokens.EDIT_GROUP_MEMBERS) @UseGatekeeper(AclGatekeeper.class) @GatekeeperParams({"GROUPS_UPDATE", "MEMBERS_UPDATE"}) interface MyProxy extends ProxyPlace<EditGroupMembersPresenter> { } /* snip */ }
  • 41. ➢ Le coeur des fonctions de GWTP : ○ Accès au lifecycle, ○ Gestion centralisée des événements ○ Possède une vue, ○ Communication vue vers presenter via un UiHandlers. ➢ Permet de développer des composants réutilisables et testables. ➢ Permet de définir des Slots pour imbriquer d’autres PresenterWidgets. Références : PresenterWidget, UiHandlers, View, OrderedSlot, SingleSlot, Slot Presenter Widgets Partie 3 GWTP
  • 42. Exemple: Un PresenterWidget Le PresenterWidget public class DashboardPresenter extends PresenterWidget<MyView> { interface MyView extends View { } static final Slot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>(); private final Presenter<?, ?> presenter; @Inject DashboardPresenter( EventBus eventBus, MyView view, Presenter<?, ?> presenter) { super(eventBus, view); this.presenter = presenter; } @Override protected void onBind() { setInSlot(SLOT_TEMPLATE, presenter); } } Sa vue public class DashboardView extends ViewImpl implements MyView { interface Binder extends UiBinder<Widget, DashboardView> { } @UiField SimplePanel template; @Inject DashboardView(Binder uiBinder) { initWidget(uiBinder.createAndBindUi(this)); bindSlot(DashboardPresenter.SLOT_TEMPLATE, template); } } Sa configuration GIN public class DashboardModule extends AbstractPresenterModule { @Override protected void configure() { bindPresenterWidget(DashboardPresenter.class, DashboardPresenter.MyView.class, DashboardView.class); } }
  • 43. ➢ Un PresenterWidget spécialisé. ➢ Doit être associé à un proxy. ➢ Initialisation uniquement lorsque nécessaire: améliore le chargement initial. ➢ Peut être associé à un name token (place). ➢ Permet de définir des NestedSlots pour imbriquer d’autres Presenters sans couplage. Références : Presenter, NestedSlot, Proxy, ProxyPlace, @ProxyStandard, @NameToken Presenter Partie 3 GWTP
  • 44. Exemple: Un Presenter Le PresenterWidget public class DashboardPresenter extends Presenter<MyView, MyProxy> { interface MyView extends View { } @ProxyStandard @NameToken(NameTokens.DASHBOARD) @UseGatekeeper(LoggedInGatekeeper.class) interface MyProxy extends ProxyPlace<DashboardPresenter> { } static final NestedSlot SLOT_TEMPLATE = new NestedSlot(); @Inject DashboardPresenter( EventBus eventBus, MyView view, MyProxy proxy) { super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN); } } public class DashboardView extends ViewImpl implements MyView { interface Binder extends UiBinder<Widget, DashboardView> { } @UiField SimplePanel template; @Inject DashboardView(Binder uiBinder) { initWidget(uiBinder.createAndBindUi(this)); bindSlot(DashboardPresenter.SLOT_TEMPLATE, template); } } Sa configuration GIN public class DashboardModule extends AbstractPresenterModule { @Override protected void configure() { bindPresenter(DashboardPresenter.class, DashboardPresenter.MyView.class, DashboardView.class, DashboardPresenter.MyProxy.class); } } Sa vue
  • 45. ➢ Un PresenterWidget ayant un popup comme vue. ➢ Les mêmes avantages qu’un PresenterWidget: slots, lifecycle. ➢ Délègue l’affichage / masquage du popup au presenter. ➢ Abstrait le concept de positionnement du popup. Références : PopupView, PopupViewImpl, addToPopupSlot, PopupPositioner Popups Partie 3 GWTP
  • 47. Partie 4 Rest-Dispatch Amené à disparaître dans GWT. Nécessite beaucoup de code pour un cas simple. Renforcit le couplage entre client et serveur. Plus simple de conserver le code client et serveur compatible. RPC REST Permet de communiquer avec n’ importe quel API REST. Presque tous les serveurs permettent de faire du REST : à la mode. Plus facile de modifier l’ API et oublier de mettre le code client à jour.
  • 48. ➢ Implémentation partielle de JSR-311 1.1 (JAX-RS). ➢ Définition d’interface et configuration de l’API au moyen d’annotations. ➢ Code client (GWT) seulement. L’API doit être codé en utilisant une autre technologie. ➢ Possibilité de créer ses propres extensions. Rest-Dispatch Partie 4 Rest-Dispatch
  • 49. ➢ Usage plus explicite que la syntaxe de base. ➢ Les interfaces et annotations peuvent être réutilisé sur l’implémentation serveur (DRY). ➢ Plus simple de conserver un API privé compatible à la fois sur le client et le serveur. ➢ Désavantage: Perte du type safety lors de la création de callbacks. Extension : Delegates Partie 4 Rest-Dispatch
  • 50. Exemple: Configurer et utiliser Rest-Dispatch Le module GWT Les paths Le module GIN L’interface de ressource <?xml version='1.0' encoding='UTF-8'?> <module> <inherits name="com.gwtplatform.dispatch.rest.delegates.ResourceDelegate"/> <inherits name='com.gwtplatform.mvp.MvpWithEntryPoint'/> <source path="client"/> </module> public class ResourcesModule extends AbstractGinModule { @Override protected void configure() { install(new RestDispatchAsyncModule.Builder() /* Additional configurations */ .build()); } @Provides @RestApplicationPath String applicationPath() { String baseUrl = GWT.getHostPageBaseURL(); if (baseUrl.endsWith("/")) baseUrl = baseUrl.substring(0, baseUrl.length() - 1); return baseUrl + API; } } public static class DashboardPaths { public static final String DASHBOARD_ID = "dashboard-id"; public static final String DASHBOARDS = "/dashboards"; public static final String DASHBOARD = "/{" + DASHBOARD_ID + "}"; } @Path(DASHBOARDS) public interface DashboardResource { @GET List<Dashboard> getAll(); @GET @Path(DASHBOARD) Dashboard get(@PathParam(DASHBOARD_ID) int id); @PUT @Path(DASHBOARD) void update(@PathParam(DASHBOARD_ID) int id, Dashboard dashboard); }
  • 51. Exemple: Configurer et utiliser Rest-Dispatch (suite) Utilisation de la ressource static final SingleSlot<PresenterWidget<?>> SLOT_TEMPLATE = new SingleSlot<>(); private final ResourceDelegate<DashboardResource> dashboards; private final DashboardTemplateFactory templateSelector; @Inject DashboardPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<DashboardResource> dashboards, DashboardTemplateFactory templateFactory) { super(eventBus, view, proxy, ApplicationPresenter.SLOT_MAIN); this.dashboards = dashboardService; this.templateFactory = templateFactory; } @Override protected void onReveal() { dashboards.withCallback(new AbstractAsyncCallback<Dashboard>() { @Override public void onSuccess(Dashboard result) { PresenterWidget<?> template = templateFactory.createTemplate(result); setInSlot(SLOT_TEMPLATE, template); } }).get(DashboardSettings.DEFAULT_DASHBOARD_ID); }
  • 52. Tests unitaires Tester GWTP et Rest-Dispatch avec Mockito et Jukito Partie 5
  • 53. Partie 5 Tests unitaires Jukito Jukito permet à vos tests d’utiliser l’ injection de dépendances. Peu importe le type de test — unitaire, intégration ou quoi que ce soit de loufoque —, le code redondant dû au mocking diminuera grandement. Rapidement, vous ne pourrez plus vous passez de sa syntaxe sécuritaire et basée sur les annotations!
  • 54. ➢ Mocking automatique ➢ Injection des mocks ➢ Annotation @ALL ➢ Léger ➢ S'adapte à toutes tailles de test ➢ Facile d’installation Fonctionalités Partie 5 Tests unitaires
  • 55. Exemples Sur GitHub Mockito et Jukito: ManufacturerDetailPresenter: https://goo. gl/27NsKL ManufacturerDetailPresenterTest: https://goo. gl/irq5rV ManufacturerDetailPresenterMockitoTest: https: //goo.gl/EIlj47 Jukito @All: PersonRenderer: https://goo.gl/o4UGZn PersonRendererTest: https://goo.gl/uhcL2k Partie 5 Tests unitaires
  • 56. Mockito : Initialisation CAS DE TEST TEST @Inject ManufacturerDetailPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<ManufacturersResource> manufacturers, PlaceManager placeManager, EditManufacturerMessages messages) { super(eventBus, view, proxy, SLOT_MAIN_CONTENT); this.manufacturers = manufacturers; this.placeManager = placeManager; this.messages = messages; getView().setUiHandlers(this); } public class ManufacturerDetailPresenterMockitoTest { // SUT private ManufacturerDetailPresenter presenter; // Mocks (created by Mockito) @Mock private EventBus eventBus; @Mock private MyView view; @Mock private MyProxy proxy; @Mock private ResourceDelegate<ManufacturersResource> manufacturers; @Mock private PlaceManager placeManager; @Before public void setUp() { // Create @Mock and @Captor fields. MockitoAnnotations.initMocks(this); // Manual way to create a mock EditManufacturerMessages messages = mock(EditManufacturerMessages.class); // Manual creation of the SUT. presenter = new ManufacturerDetailPresenter(eventBus, view, proxy, manufacturers, placeManager, messages); }
  • 57. Mockito : Test CAS DE TEST TEST @Override protected void onReveal() { List<ActionType> actions; if (createNew) { actions = Arrays.asList(ActionType.DONE); } else { actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE); } ChangeActionBarEvent.fire(this, actions, false); } @Test public void onReveal() { // when presenter.onReveal(); // then // Manual way to create an argument captor ArgumentCaptor<ChangeActionBarEvent> captor = ArgumentCaptor.forClass(ChangeActionBarEvent.class); verify(eventBus) .fireEventFromSource(captor.capture(), same(presenter)); ChangeActionBarEvent event = captor.getValue(); assertThat(event).isNotNull(); assertThat(event.getActions()).containsOnly(ActionType.DONE); assertThat(event.getTabsVisible()).isFalse(); }
  • 58. Jukito : Initialisation CAS DE TEST TEST @RunWith(JukitoRunner.class) public class ManufacturerDetailPresenterTest { // SUT (Injected by Jukito). We don't need to create all dependencies explicitly, Jukito will mock them. @Inject private ManufacturerDetailPresenter presenter; // Mocks (injected by Jukito) @Inject private EventBus eventBus; // Captors (Create by Mockito) @Captor private ArgumentCaptor<ChangeActionBarEvent> changeActionBarEventCaptor; @Before public void setUp() { // Create @Mock and @Captor fields. MockitoAnnotations.initMocks(this); } @Inject ManufacturerDetailPresenter( EventBus eventBus, MyView view, MyProxy proxy, ResourceDelegate<ManufacturersResource> manufacturers, PlaceManager placeManager, EditManufacturerMessages messages) { super(eventBus, view, proxy, SLOT_MAIN_CONTENT); this.manufacturers = manufacturers; this.placeManager = placeManager; this.messages = messages; getView().setUiHandlers(this); }
  • 59. Jukito : Test CAS DE TEST TEST @Override protected void onReveal() { List<ActionType> actions; if (createNew) { actions = Arrays.asList(ActionType.DONE); } else { actions = Arrays.asList(ActionType.DELETE, ActionType.UPDATE); } ChangeActionBarEvent.fire(this, actions, false); } @Test public void onReveal_newManufacturer_preparesActionBar() { // when presenter.onReveal(); // then verify(eventBus) .fireEventFromSource(changeActionBarEventCaptor.capture(), same(presenter)); ChangeActionBarEvent event = changeActionBarEventCaptor.getValue(); assertThat(event).isNotNull(); assertThat(event.getActions()).containsOnly(ActionType.DONE); assertThat(event.getTabsVisible()).isFalse(); }
  • 60. Jukito : Utiliser @All pour tester plusieurs cas CAS DE TEST TEST @Inject PersonRenderer( Messages messages, @Assisted Mode mode) { this.mode = mode; this.messages = messages; } public String render(Person person) { String result = ""; if (person != null) { String firstName = person.getFirstName(); String middleName = person.getMiddleName(); String lastName = person.getLastName(); String title = renderTitle(person); if (mode.isDisplayTitle() && !Strings.isNullOrEmpty(title)) { result += title + " "; } if (mode.isDisplayFirstName() && !Strings.isNullOrEmpty(firstName)) { result += firstName + " "; } if (mode.isDisplayMiddleName() && !Strings.isNullOrEmpty(middleName)) { result += middleName + " "; } if (mode.isDisplayLastName() && !Strings.isNullOrEmpty(lastName)) { result += lastName; } } if (result.isEmpty()) { result = messages.unknown(); } return result; } public class PersonNameTestCase { private final Person person; private Mode mode; private String expected; public PersonNameTestCase() { person = null; } public PersonNameTestCase( String firstName, String lastName) { person = new Person(firstName, lastName); } /* Setters */ public Person getPerson() { return person; } public Mode getMode() { return mode; } public String getExpected() { return expected; } }
  • 61. Jukito : Utiliser @All pour tester plusieurs cas (suite) TEST TEST @RunWith(JukitoRunner.class) public class PersonRendererTest { public static class Module extends JukitoModule { @Override protected void configureTest() { bindManyInstances(PersonNameTestCase.class, new PersonNameTestCase("Zom", "Bee") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MR) .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").middleName("Buzz") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MR).middleName("Buzz") .mode(Mode.SHORT).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee") .mode(Mode.FORMAL).expected("Zom Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MS) .mode(Mode.FORMAL).expected("Ms. Zom Bee"), new PersonNameTestCase("Zom", "Bee").middleName("Buzz") .mode(Mode.FORMAL).expected("Zom Buzz Bee"), new PersonNameTestCase("Zom", "Bee").title(Title.MS).middleName("Buzz") .mode(Mode.FORMAL).expected("Ms. Zom Buzz Bee"), new PersonNameTestCase() .mode(Mode.SHORT).expected(UNKNOWN), new PersonNameTestCase() .mode(Mode.FORMAL).expected(UNKNOWN) ); } } private static final String UNKNOWN = "unknown"; @Inject private Messages messages; @Before public void setUp() { given(messages.unknown()).willReturn(UNKNOWN); given(messages.title(Title.MR)).willReturn("Mr."); given(messages.title(Title.MS)).willReturn("Ms."); } @Test public void render(@All PersonNameTestCase testCase) { // given Person person = testCase.getPerson(); Mode mode = testCase.getMode(); PersonRenderer renderer = new PersonRenderer(messages, mode); // when String result = renderer.render(person); // then String expected = testCase.getExpected(); assertThat(result).overridingErrorMessage( "Expected %s with mode %s to be '%s'.", person, mode, expected) .isEqualTo(expected); }
  • 62. Jukito : Utilitaires pour tester Rest-Dispatch CAS DE TEST TEST public void onSave(ManufacturerDto manufacturer) { manufacturersDelegate .withCallback(new AbstractAsyncCallback<ManufacturerDto>(this) { @Override public void onSuccess(ManufacturerDto newManufacturer) { DisplayMessageEvent.fire(ManufacturerDetailPresenter.this, new Message(messages.manufacturerSaved(), SUCCESS)); PlaceRequest placeRequest = new Builder() .nameToken(NameTokens.MANUFACTURER) .build(); placeManager.revealPlace(placeRequest); } }) .saveOrCreate(manufacturerDto); } @Test public void onSave_showsMessage_revealsManufacturers( EditManufacturerMessages messages) { // given ManufacturerDto manufacturer = new ManufacturerDto(); ManufacturerDto resultManufacturer = new ManufacturerDto(); givenDelegate(manufacturersResource) .succeed().withResult(resultManufacturer) .when().saveOrCreate(same(manufacturer)); given(messages.manufacturerSaved()).willReturn(A_MESSAGE); // when presenter.onSave(manufacturer); // then // note `isA` is used instead of `any`. This is because the event bus // accepts all `GwtEvent` subclasses. `isA` also verifies the type verify(eventBus) .fireEventFromSource(isA(DisplayMessageEvent.class),same(presenter)); verify(placeManager).revealPlace(placeRequestCaptor.capture()); PlaceRequest placeRequest = placeRequestCaptor.getValue(); assertThat(placeRequest.getNameToken()) .isEqualTo(NameTokens.MANUFACTURER); }
  • 64. Avoid widgets as much as you can BEST PRACTICE 1 Partie 6
  • 66.
  • 68.
  • 69.
  • 70.
  • 71. Full Event Mechanism BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  • 72. Even if you don’t need it Full Event Mechanism BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  • 73. How to attach event handler to elements ? BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  • 74.
  • 75. Use widget To encapsulate complex component to reuse » prefer PresenterWidget if the component has a lot of business logic » In the futur: Web Component. BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  • 76. Cell widgets (CellTable, CellList…) HtmlPanel Exceptions BP 1 : AVOID WIDGETS AS MUCH AS YOU CAN Partie 6
  • 77. Use an Event Bus BEST PRACTICE 2 Partie 6
  • 81. Use directly your name tokens BP 5 : MAKE NAVIGATION EASY Partie 6
  • 82. Use CSS as much as you can BEST PRACTICE 6 Partie 6
  • 83. Loas everything you can in the html page BEST PRACTICE 7 Partie 6
  • 85. Test each protected and public methods of your presenters. UNIT TESTS Partie 6
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 92. Improve quality of your code CODE REVIEW Partie 6
  • 94. A way to learn from your peers CODE REVIEW Partie 6
  • 95. Web based code review system CODE REVIEW Partie 6
  • 96.
  • 98.
  • 99. TAKE CONTROL OF YOUR HTML!USE A STYLE GUIDE !
  • 100. Address/fix all comments before you merge. CODE REVIEW Best Practice Partie 6
  • 103. The first to do code review is your CI Server CONTINUOUS INTEGRATION Partie 6
  • 120. Export any Java code to Javascript JSinterop Partie 7
  • 121. Export any Javascript API to Java Partie 7 JSinterop
  • 123. Web component Material design in GWT Partie 7 JSinterop
  • 124. Start to experiment with GWT 2.7 » Turn it on with a flag -XjsInteropMode » Subject to change ! Partie 7 JSinterop
  • 125. MERCI