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
190
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
生成AIでGitHubソースコード取得して仕様書を作成
shukob
0
550
Внедряем бюджетирование, или Как сделать хорошо?
lamodatech
0
660
Cloudflare MCP ServerでClaude Desktop からWeb APIを構築
kutakutat
1
580
iOS開発におけるCopilot For XcodeとCode Completion / copilot for xcode
fuyan777
1
270
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
170
Beyond ORM
77web
9
1.3k
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
350
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
4
930
create_tableをしただけなのに〜囚われのuuid編〜
daisukeshinoku
0
290
20年もののレガシープロダクトに 0からPHPStanを入れるまで / phpcon2024
hirobe1999
0
860
どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか
okashoi
3
640
LLM Supervised Fine-tuningの理論と実践
datanalyticslabo
7
1.6k
Featured
See All Featured
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
2
300
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
Statistics for Hackers
jakevdp
796
220k
Writing Fast Ruby
sferik
628
61k
Making the Leap to Tech Lead
cromwellryan
133
9k
Unsuck your backbone
ammeep
669
57k
Learning to Love Humans: Emotional Interface Design
aarron
274
40k
The Invisible Side of Design
smashingmag
298
50k
Reflections from 52 weeks, 52 projects
jeffersonlam
347
20k
A Philosophy of Restraint
colly
203
16k
Testing 201, or: Great Expectations
jmmastey
41
7.1k
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!