Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
AngularJS Aplicado — QCon Rio 2014
Search
Felippe Nardi
September 25, 2014
Programming
1
200
AngularJS Aplicado — QCon Rio 2014
Construindo aplicações client-side bem testadas
Felippe Nardi
September 25, 2014
Tweet
Share
More Decks by Felippe Nardi
See All by Felippe Nardi
Trabalhando Remoto como Desenvolvedor Web
felippenardi
2
170
Other Decks in Programming
See All in Programming
NPOでのDevinの活用
codeforeveryone
0
790
関数型まつりレポート for JuliaTokai #22
antimon2
0
160
スタートアップの急成長を支えるプラットフォームエンジニアリングと組織戦略
sutochin26
0
4.2k
dbt民主化とLLMによる開発ブースト ~ AI Readyな分析サイクルを目指して ~
yoshyum
3
800
#QiitaBash MCPのセキュリティ
ryosukedtomita
0
970
datadog dash 2025 LLM observability for reliability and stability
ivry_presentationmaterials
0
460
今ならAmazon ECSのサービス間通信をどう選ぶか / Selection of ECS Interservice Communication 2025
tkikuc
21
3.9k
『自分のデータだけ見せたい!』を叶える──Laravel × Casbin で複雑権限をスッキリ解きほぐす 25 分
akitotsukahara
2
620
PHPで始める振る舞い駆動開発(Behaviour-Driven Development)
ohmori_yusuke
2
330
What Spring Developers Should Know About Jakarta EE
ivargrimstad
0
420
技術同人誌をMCP Serverにしてみた
74th
1
630
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
260
Featured
See All Featured
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
How to Ace a Technical Interview
jacobian
277
23k
Navigating Team Friction
lara
187
15k
The Straight Up "How To Draw Better" Workshop
denniskardys
234
140k
The World Runs on Bad Software
bkeepers
PRO
69
11k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.4k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
Six Lessons from altMBA
skipperchong
28
3.9k
The Cult of Friendly URLs
andyhume
79
6.5k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
138
34k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Transcript
AngularJS aplicado Construindo aplicações client-side bem testadas @FelippeNardi
Objetivos ! • Elementos essenciais! • Testes: e2e e unitários
• Mindset AngularJS
Felippe Nardi Front-end Engineer e Designer
client-side web apps
client-side web apps
None
Service Controller View
Angular vai te ajudar: • Organizar o javascript • Criar
sites responsivos •Testar com facilidade
Framework Javascript
Extensão do HTML
None
<input type=“text” autofocus>
Service Controller View Directives Expressions
ng-model <input ng-model="teste">! Hello {{ teste }}!
AngularJS Digest Loop
Iniciando a aplicação <html ng-app>! !<!-- (...) -->! </html>!
<input ng-model="teste"> Hello {{ teste }}! <span ng-hide="!teste">! ! </span>
<input ng-model="teste"> <span ng-show="teste">! ! </span> Hello {{ teste }}!
Recapitulando • Directives • {{ Expressions }} • Digest Loop
Service Controller View Directives Expressions
Service Controller View Directives Expressions Filters
Filters {{"Olá"}}!
Filters {{"Olá"|uppercase}}!
Filters {{"Olá"|lowercase}}!
Filters <input ng-model="busca">! ! {{users|filter:busca}}!
Recapitulando • {{ expression | filter }} • Array •
String
I18n {{1228212|number}}!
I18n {{12.82|currency}}!
{{1411676100000! |date}}! I18n
Suporte pt-BR <script src="angular.js">! <script src="angular- locale_pt-br.js">! Todos os arquivos
de locale do Angular
{{1228212|number}}! I18n
{{12.82|currency}}! I18n
{{1411676100000! |date}}! I18n
Indo além angular-translate github.com/angular-translate/angular-translate
Service Controller View Directives Expressions
function MyCtrl($scope){! !$scope.teste ="Olá!";! };! Controllers
<div! ng-controller="MyCtrl">! !{{ teste }}! </div>! function MyCtrl($scope){! !$scope.teste ="Olá!";!
};!
Directives Essenciais
ng-repeat <li ng-repeat="name in names">! !{{ name }}! </li>! $scope.names
= [! !"Antônio", "Carlos", "Souza"! ];!
ng-pluralize <ng-pluralize count="names"! when="{'0':'Nenhum nome',! ! '1':'Só um nome',! !
'other': '{} nomes'}">! </ng-pluralize>!
ng-pluralize <ng-pluralize count="names"! when="{'0':'Nenhum nome',! ! '1':'Só um nome',! !
'other': '{} nomes'}">! </ng-pluralize>!
Recapitulando • Controllers • $scope • ng-controller
Service Controller View Directives Expressions
function MyCtrl( ){! ! };! $scope $scope.teste ="World!";! Service
$filter('uppercase') ("Hello");! {{"Hello"|uppercase}}! Service
$http({ method: 'GET',! url: '/home' })! XMLHttpRequest ou JSONP .then(!
! ! ); !function() { /*...*/ },! !function() { /*...*/} Service
Recapitulando: • Data-binding! • Digest Loop! • Dependency Injection
Unit Tests! no AngularJS
TestRunner.html: • jasmine-all.js • angular.js • angular-mocks.js • arquivos javascript
• arquivos de test
moourl.com/tryangular
2 exemplos! Service Controller
None
github.com/felippenardi/ mini-lero-lero
Nomeando o App var leroLeroApp = angular.module('leroLeroApp', []);! app.js
<html ng-app="leroLeroApp">! !<!-- (...) -->! </html>! Nomeando o App index.html
Carregando as frases app.js
Service app.js
leroLeroApp.factory('geradorDeFrases',! ! function ($http) {! ! var promise =! !
! $http.get('frases.json')! .then(function (response) {! return response.data;! });! ! return {! get: function() {! return promise;! }! };! });
Syntax Jasmine describe('Descrição', function() {! ! beforeEach(function() {! ! !
// Roda antes de cada teste! });! ! afterEach(function() {! ! ! // Roda depois de cada teste! });! !
Testando o Service appSpecs.js
}); describe('Service: Gerador De Frases', function() {! ! ! !
! ! ! ! ! ! ! ! ! ! var geradorDeFrases,! ! ! ! httpBackend,! ! ! ! frases; beforeEach(module('leroLeroApp')); ! ! beforeEach(inject(! ! ! ! ! ! ));! ! ! function(_geradorDeFrases_,! ! ! ! ! ! ! ! ! $httpBackend) {! ! ! ! ! } ! ! ! geradorDeFrases = _geradorDeFrases_;! ! ! ! httpBackend = $httpBackend;
}); describe('Service: Gerador De Frases', function() {! ! ! !
! ! ! ! ! ! ! ! ! ! var geradorDeFrases,! ! ! ! httpBackend,! ! ! ! frases; beforeEach(module('leroLeroApp')); ! ! beforeEach(inject(! ! ! ! ! ! ));! ! ! function(_geradorDeFrases_,! ! ! ! ! ! ! ! ! $httpBackend) {! ! ! ! ! } ! ! ! geradorDeFrases = _geradorDeFrases_;! ! ! ! httpBackend = $httpBackend;
}); afteEach(function() {! ! ! httpBackend! .verifyNoOutstandingExpectation();! httpBackend! .verifyNoOutstandingRequest();! });
! ! ! ! ! ! ! ! ! beforeEach(inject(! ! ! ! ! ! ));! ! ! function(_geradorDeFrases_,! ! ! ! ! ! ! ! ! $httpBackend) {! ! ! ! ! } ! ! ! geradorDeFrases = _geradorDeFrases_;! ! ! ! httpBackend = $httpBackend;
}); ! ! ! ! ! ! ! ! !
! beforeEach(inject(! ! ! ! ! ! ));! ! ! function(_geradorDeFrases_,! ! ! ! ! ! ! ! ! $httpBackend) {! ! ! ! ! } ! ! ! geradorDeFrases = _geradorDeFrases_;! ! ! ! httpBackend = $httpBackend; afteEach(function() {! ! ! httpBackend! .verifyNoOutstandingExpectation();! httpBackend! .verifyNoOutstandingRequest();! });
it('fornece frases', function() {! ! ! ! ! ! !
! ! ! ! ! ! }); httpBackend! .expectGET('frases.json').respond([! "Frase 1", "Frase 2", "Frase 3"! ]); geradorDeFrases.get()! .then(function(response) {! frases = response;! }); expect(frases)! .toEqual(jasmine.any(Array)); expect(frases.length).toBe(3);
None
it('fornece frases', function() {! ! ! ! ! ! !
! ! ! ! ! ! }); httpBackend! .expectGET('frases.json').respond([! "Frase 1", "Frase 2", "Frase 3"! ]); geradorDeFrases.get()! .then(function(response) {! frases = response;! }); httpBackend.flush(); expect(frases)! .toEqual(jasmine.any(Array)); expect(frases.length).toBe(3);
None
Recapitulando • Carregar a aplicação • Injetar serviços • Usar
variáveis para armazenar os serviços
Mostrando as frases index.html
frase.gerar()
Expor objeto frases pra view app.js
Controller app.js
leroLeroApp.controller('MainCtrl',! ! function ($scope, geradorDeFrases) {! ! ! ! var
i = 0, frases;! geradorDeFrases.get()! .then(function(response){! frases = response;! $scope.frase.gerar();! });! $scope.frase = {! gerar: function() {! $scope.frase.atual = frases[i];! i < frases.length - 1? i++ : i = 0;! }! };! });
Testando o Controller appSpecs.js
describe('Controller: MainCtrl',! function() {! var scope, MainCtrl, geradorMock, q;! !
beforeEach(module('leroLeroApp'));! ! beforeEach(inject(! function($controller, $rootScope, $q) {! q = $q;! scope = $rootScope.$new();! ! MainCtrl = $controller('MainCtrl', {! $scope: scope,! geradorDeFrases: geradorMock! });
! beforeEach(inject(! function($controller, $rootScope, $q) {! q = $q;! scope
= $rootScope.$new();! ! MainCtrl = $controller('MainCtrl', {! $scope: scope,! geradorDeFrases: geradorMock! }); ! scope.$apply();! }! ));!
geradorMock = {! get: function() {! var frases = q.defer();!
frases.resolve(["A","B","C"]);! return frases.promise;! }! };! ! it('começa com uma frase', function() {! expect(scope.frase.atual)! .toEqual(jasmine.any(String));! });
! it('começa com uma frase', function() {! expect(scope.frase.atual)! .toEqual(jasmine.any(String));! });
! it('gera nova a frase', function() {! var primeiraFrase = scope.frase.atual;! ! scope.frase.gerar();! ! var segundaFrase = scope.frase.atual;! ! expect(primeiraFrase)! .not.toEqual(segundaFrase);! });
it('gera infinitas frases', function() {! var i = 4;! do
{ scope.frase.gerar() } while (--i);! ! expect(scope.frase.atual! ! ! .toBeDefined();! });! });! ! scope.frase.gerar();! ! var segundaFrase = scope.frase.atual;! ! expect(primeiraFrase)! .not.toEqual(segundaFrase);! });
None
frase.gerar()
<div ng-controller="MainCtrl">! ! <h1>Lero Lero</h1>! ! <a ng-click=" ">! Gerar
frase! </a>! ! <blockquote>! ! </blockquote>! ! </div>! <div ng-controller="MainCtrl">! ! <h1>Lero Lero</h1>! ! <a ng-click="frase.gerar()">! Gerar frase! </a>! ! <blockquote>! {{ frase.atual }}! </blockquote>! ! </div>!
None
Recapitulando • Mesma estrutura básica! • Sobrescrever serviços! • Simular
promessas
Testes Unitários! prontos
Vamos para os Testes End to End!
None
None
1 teste:! gera frase
Setup do Protractor $ npm install -g protractor $ webdriver-manager
update $ webdriver-manager start
$ webdriver-manager start http://localhost:4444/wd/hub
Arquivo de configuração // conf.js! exports.config = {! seleniumAddress:! '
',! ! }! $ protractor conf.js http://localhost:4444/wd/hub specs: ['spec.js']!
beforeEach(function() {! browser.get('http://localhost:8000/');! });! ! describe('Frase', function() {! ! it('gera
frase', function() {! var frase1,! frase2;! ! element(by.binding('frase.atual')).getText()! .then(function(frase) {! frase1 = frase;! });! ! element(by.id('gerar-frase')).click();
it('gera frase', function() {! var frase1,! frase2;! ! element(by.binding('frase.atual')).getText()! .then(function(frase)
{! frase1 = frase;! });! ! element(by.id('gerar-frase')).click(); element(by.binding('frase.atual')).getText()! .then(function(frase) {! frase2 = frase;! expect(frase1).not.toBe(frase2);! });! });! });
None
None
None
Recapitulando • Rodar servidor selenium! • Preparar arquivo conf.js! •
Rodar protractor conf.js
Seus próximos passos:
github.com/felippenardi/mini-lero-lero
None
moourl.com/tryangular
None
moourl.com/tryangular
angular.github.io/protractor/
angular.github.io/protractor/
jasmine.github.io
jasmine.github.io
moourl.com/angularjs
youtube: A Tiny Piece of AngularJS
egghead.io
@felippenardi Obrigado!