socialgekon.com
  • Põhiline
  • Kujundusprotsess
  • Vilgas Talent
  • Töö Tulevik
  • Toote Elutsükkel
Tagumine Ots

Töötamine TypeScripti, sõltuvuse süstimise ja ebakõla robotitega

Tüübid ja testitav kood on kaks kõige tõhusamat viisi vigade vältimiseks, eriti kui kood aja jooksul muutub. Neid kahte tehnikat saame JavaScripti arendamisel rakendada, kasutades selleks vastavalt TypeScripti ja sõltuvuse süstimise (DI) kujundusmustrit.

Selles TypeScripti õpetuses ei käsitleta otseselt TypeScripti põhitõdesid, välja arvatud kompileerimine. Selle asemel demonstreerime lihtsalt TypeScripti parimaid tavasid, kui käime läbi, kuidas Discordi bot nullist teha, teste ja DI siduda ning prooviteenust luua. Kasutame:

  • Node.js
  • TypeScript
  • Discord.js, Discordi API ümbris
  • InversifyJS, sõltuvuse süstimise raamistik
  • Raamatukogude testimine: Mocha, Chai ja ts-mockito
  • Boonus: Mongoose ja MongoDB integratsioonitesti kirjutamiseks

Node.js projekti seadistamine

Kõigepealt loome uue kataloogi nimega typescript-bot Seejärel sisestage see ja looge uus Node.js projekt, käivitades:



npm init

Märkus. Võite kasutada ka yarn selleks, kuid jäägem npm juurde lühiduse pärast.

See avab interaktiivse viisardi, mis seadistab package.json faili. Võite lihtsalt vajutada Sisenema kõigi küsimuste jaoks (või esitage soovi korral teavet). Seejärel installime oma sõltuvused ja dev-sõltuvused (need, mida on vaja ainult testide jaoks).

npm i --save typescript discord.js inversify dotenv @types/node reflect-metadata npm i --save-dev chai mocha ts-mockito ts-node @types/chai @types/mocha

Seejärel asendage loodud 'scripts' jaotis package.json koos:

'scripts': { 'start': 'node src/index.js', 'watch': 'tsc -p tsconfig.json -w', 'test': 'mocha -r ts-node/register 'tests/**/*.spec.ts'' },

Topeltpakkumised tests/**/*.spec.ts ümber on vaja failide rekursiivseks leidmiseks. (Märkus: Windowsi kasutavate arendajate puhul võib süntaks erineda.)

start roboti käivitamiseks kasutatakse skripti watch skript TypeScripti koodi kompileerimiseks ja test testide läbiviimiseks.

Nüüd on meie package.json fail peaks välja nägema selline:

{ 'name': 'typescript-bot', 'version': '1.0.0', 'description': '', 'main': 'index.js', 'dependencies': { '@types/node': '^11.9.4', 'discord.js': '^11.4.2', 'dotenv': '^6.2.0', 'inversify': '^5.0.1', 'reflect-metadata': '^0.1.13', 'typescript': '^3.3.3' }, 'devDependencies': { '@types/chai': '^4.1.7', '@types/mocha': '^5.2.6', 'chai': '^4.2.0', 'mocha': '^5.2.0', 'ts-mockito': '^2.3.1', 'ts-node': '^8.0.3' }, 'scripts': { 'start': 'node src/index.js', 'watch': 'tsc -p tsconfig.json -w', 'test': 'mocha -r ts-node/register 'tests/**/*.spec.ts'' }, 'author': '', 'license': 'ISC' }

Uue rakenduse loomine Discordi rakenduste juhtpaneelil

Discordi API-ga suhtlemiseks vajame märki. Sellise märgi genereerimiseks peame rakenduse registreerima Discordi arendaja juhtpaneelil. Selleks peate looma ebakõla konto ja minema https://discordapp.com/developers/applications/ . Seejärel klõpsake nuppu Uus rakendus nupp:

Ebakõla

Valige nimi ja klõpsake nuppu Loo . Seejärel klõpsake nuppu Bot → Lisage Bot ja olete valmis. Lisame roboti serverisse. Kuid ärge seda lehte veel sulgege, peame varsti kopeerima märgise.

Lisage oma ebakõla bot oma serverisse

Meie roboti testimiseks vajame Discordi serverit. Võite kasutada olemasolevat serverit või luua uue. Selleks kopeerige roboti CLIENT_ID - leitud vahekaardilt Üldteave - ja kasutage seda selle osana eriluba URL:

https://discordapp.com/oauth2/authorize?client_id=&scope=bot

Kui klõpsate seda URL-i brauseris, kuvatakse vorm, kus saate valida serveri, kuhu robot tuleks lisada.

Tavaline ebakõla tervitussõnum vastuseks meie roboti serveriga liitumisele.

Pärast roboti lisamist oma serverisse peaksite nägema ülaltoodud sõnumit.

.env Loomine Fail

Vajame mingit viisi, kuidas märk oma rakendusse salvestada. Selleks kasutame dotenv pakend. Esmalt hankige märgis Discord Application Dashboardilt ( Bot → Klõpsake märgi paljastamiseks ):

The

Nüüd looge .env fail, seejärel kopeerige ja kleepige märk siia:

TOKEN=paste.the.token.here

Kui kasutate Giti, peaks see fail olema paigutatud kausta .gitignore, et luba ei kahjustataks. Looge ka .env.example faili, nii et on teada, et TOKEN vajab määratlust:

TOKEN=

TypeScripti kompileerimine

TypeScripti kompileerimiseks võite kasutada npm run watch käsk. Teise võimalusena, kui kasutate PHPStormi (või mõnda muud IDE-d), kasutage lihtsalt oma TypeScripti pistikprogrammi failivaaturit ja laske oma IDE-l kompileerida. Testime oma seadistust, luues src/index.ts fail sisuga:

console.log('Hello')

Looge ka tsconfig.json fail nagu allpool. InversifyJS nõuab experimentalDecorators, emitDecoratorMetadata, es6 ja reflect-metadata

{ 'compilerOptions': { 'module': 'commonjs', 'moduleResolution': 'node', 'target': 'es2016', 'lib': [ 'es6', 'dom' ], 'sourceMap': true, 'types': [ // add node as an option 'node', 'reflect-metadata' ], 'typeRoots': [ // add path to @types 'node_modules/@types' ], 'experimentalDecorators': true, 'emitDecoratorMetadata': true, 'resolveJsonModule': true }, 'exclude': [ 'node_modules' ] }

Kui failivaatleja töötab korralikult, peaks see genereerima src/index.js fail ja töötab npm start tulemuseks peaks olema:

> node src/index.js Hello

Boti klassi loomine

Nüüd hakkame lõpuks kasutama TypeScripti kõige kasulikumat funktsiooni: tüübid. Jätkake ja looge järgmine src/bot.ts fail:

import {Client, Message} from 'discord.js'; export class Bot { public listen(): Promise { let client = new Client(); client.on('message', (message: Message) => {}); return client.login('token should be here'); } }

Nüüd näeme siin vajalikku: märki! Kas me kopeerime selle lihtsalt siia või laadime väärtuse otse keskkonnast?

Kumbagi. Selle asemel kirjutagem hooldatavam, laiendatavam ja testitavam kood, sisestades märgi meie valitud sõltuvuse süstimise raamistiku InversifyJS abil.

Samuti näeme, et Client sõltuvus on kodeeritud. Süstime ka seda.

Sõltuvuse süstekonteineri konfigureerimine

TO sõltuvuse süstemahuti on objekt, mis teab, kuidas teisi objekte eksponeerida. Tavaliselt määratleme sõltuvused iga klassi jaoks ja DI konteiner hoolitseb nende lahendamise eest.

InversifyJS soovitab panna sõltuvused tähele inversify.config.ts faili, nii et jätkame ja lisame sinna oma DI-konteineri:

import 'reflect-metadata'; import {Container} from 'inversify'; import {TYPES} from './types'; import {Bot} from './bot'; import {Client} from 'discord.js'; let container = new Container(); container.bind(TYPES.Bot).to(Bot).inSingletonScope(); container.bind(TYPES.Client).toConstantValue(new Client()); container.bind(TYPES.Token).toConstantValue(process.env.TOKEN); export default container;

Samuti soovitavad dokumendid InversifyJS luues types.ts fail ja loetlege kõik tüübid, mida kavatseme kasutada, koos seotud Symbol See on üsna ebamugav, kuid see tagab, et meie rakenduse kasvades ei esine kokkupõrkeid. Iga Symbol on kordumatu identifikaator, isegi kui selle kirjelduse parameeter on sama (parameeter on mõeldud ainult silumiseks).

export const TYPES = { Bot: Symbol('Bot'), Client: Symbol('Client'), Token: Symbol('Token'), };

Kasutamata Symbol s, näeb see välja, kui juhtub nimede kokkupõrge:

Error: Ambiguous match found for serviceIdentifier: MessageResponder Registered bindings: MessageResponder MessageResponder

Sel hetkel on see isegi rohkem ebamugav välja selgitada, millised MessageResponder tuleks kasutada, eriti kui meie DI konteiner kasvab suureks. Symbol S kasutamine hoolitseb selle eest ja me ei ole välja mõelnud kummalisi stringiliitaleid, kui meil on kaks samanimelist klassi.

Konteineri kasutamine rakenduses Discord Bot

Muutkem nüüd oma Bot klassi konteineri kasutamiseks. Peame lisama @injectable ja @inject() märkused selleks. Siin on uus Bot klass:

import {Client, Message} from 'discord.js'; import {inject, injectable} from 'inversify'; import {TYPES} from './types'; import {MessageResponder} from './services/message-responder'; @injectable() export class Bot { private client: Client; private readonly token: string; constructor( @inject(TYPES.Client) client: Client, @inject(TYPES.Token) token: string ) { this.client = client; this.token = token; } public listen(): Promise { this.client.on('message', (message: Message) => { console.log('Message received! Contents: ', message.content); }); return this.client.login(this.token); } }

Lõpuks kiirendame oma roboti index.ts -s fail:

require('dotenv').config(); // Recommended way of loading dotenv import container from './inversify.config'; import {TYPES} from './types'; import {Bot} from './bot'; let bot = container.get(TYPES.Bot); bot.listen().then(() => { console.log('Logged in!') }).catch((error) => { console.log('Oh no! ', error) });

Nüüd käivitage robot ja laske see oma serverisse lisada. Siis, kui sisestate sõnumi serverikanalisse, peaks see ilmuma käsurea logides järgmiselt:

> node src/index.js Logged in! Message received! Contents: Test

Lõpuks on meil loodud alused: TypeScripti tüübid ja sõltuvuse süstimiskonteiner meie roboti sees.

Äriloogika juurutamine

Läheme otse selle artikli tuumani: testitava koodibaasi loomine. Lühidalt öeldes peaks meie koodeks rakendama parimaid tavasid (nt TAHKE ), mitte varjata sõltuvusi, mitte kasutada staatilisi meetodeid.

Samuti see ei tohiks joostes põhjustada kõrvaltoimeid ja olla kergesti mõnitatav .

Lihtsuse huvides teeb meie bot ainult ühte asja: see otsib sissetulevaid sõnumeid ja kui see sisaldab sõna 'ping', siis kasutame ühte saadaolevatest Discordi botikäskudest, et bot reageeriks sõnaga 'pong! ” sellele kasutajale.

Selleks, et näidata, kuidas kohandatud objekte Bot -i sisestada objekt ja testige neid ühikutega, loome kaks klassi: PingFinder ja MessageResponder. Süstime MessageResponder sisse Bot klassi ja PingFinder sisse MessageResponder.

Siin on src/services/ping-finder.ts fail:

import {injectable} from 'inversify'; @injectable() export class PingFinder { private regexp = 'ping'; public isPing(stringToSearch: string): boolean { return stringToSearch.search(this.regexp) >= 0; } }

Seejärel süstime selle klassi src/services/message-responder.ts fail:

import {Message} from 'discord.js'; import {PingFinder} from './ping-finder'; import {inject, injectable} from 'inversify'; import {TYPES} from '../types'; @injectable() export class MessageResponder { private pingFinder: PingFinder; constructor( @inject(TYPES.PingFinder) pingFinder: PingFinder ) { this.pingFinder = pingFinder; } handle(message: Message): Promise { if (this.pingFinder.isPing(message.content)) { return message.reply('pong!'); } return Promise.reject(); } }

Lõpuks on siin muudetud Bot klass, mis kasutab MessageResponder klass:

import {Client, Message} from 'discord.js'; import {inject, injectable} from 'inversify'; import {TYPES} from './types'; import {MessageResponder} from './services/message-responder'; @injectable() export class Bot { private client: Client; private readonly token: string; private messageResponder: MessageResponder; constructor( @inject(TYPES.Client) client: Client, @inject(TYPES.Token) token: string, @inject(TYPES.MessageResponder) messageResponder: MessageResponder) { this.client = client; this.token = token; this.messageResponder = messageResponder; } public listen(): Promise { this.client.on('message', (message: Message) => { if (message.author.bot) { console.log('Ignoring bot message!') return; } console.log('Message received! Contents: ', message.content); this.messageResponder.handle(message).then(() => { console.log('Response sent!'); }).catch(() => { console.log('Response not sent.') }) }); return this.client.login(this.token); } }

Selles olekus ei õnnestu rakendust käivitada, kuna MessageResponder jaoks pole definitsioone ja PingFinder klassides. Lisame inversify.config.ts -i juurde järgmise fail:

container.bind(TYPES.MessageResponder).to(MessageResponder).inSingletonScope(); container.bind(TYPES.PingFinder).to(PingFinder).inSingletonScope();

Lisame ka tüübile sümbolid types.ts:

MessageResponder: Symbol('MessageResponder'), PingFinder: Symbol('PingFinder'),

Nüüd, pärast meie rakenduse taaskäivitamist, peaks bot vastama igale sõnumile, mis sisaldab pingi:

Sõna sisaldavale sõnumile reageeriv robot

Ja kuidas see logides välja näeb:

> node src/index.js Logged in! Message received! Contents: some message Response not sent. Message received! Contents: message with ping Ignoring bot message! Response sent!

Üksuste testide loomine

Nüüd, kui sõltuvused on meile õigesti süstitud, on ühikutestide kirjutamine lihtne. Selleks kasutame Chai ja ts-mockitot; siiski on palju teisi testijooksjaid ja pilkavaid raamatukogusid, mida saaksite kasutada.

Ts-mockito pilkav süntaks on üsna sõnakas, kuid samas ka hõlpsasti mõistetav. Siit saate teada, kuidas seadistada MessageResponder teenust ja süstige PingFinder mõnitama seda:

let mockedPingFinderClass = mock(PingFinder); let mockedPingFinderInstance = instance(mockedPingFinderClass); let service = new MessageResponder(mockedPingFinderInstance);

Nüüd, kui meil on mõnitused paika pandud, saame määratleda, millise tulemuse isPing() kõned peaksid olema ja kontrollige reply() kõned. Asi on selles, et ühikutestides määratleme isPing() tulemuse helista: true või false. Pole tähtis, mis on sõnumi sisu, nii et testides kasutame lihtsalt 'Non-empty string'

when(mockedPingFinderClass.isPing('Non-empty string')).thenReturn(true); await service.handle(mockedMessageInstance) verify(mockedMessageClass.reply('pong!')).once();

Kogu testikomplekt võiks välja näha nii:

import 'reflect-metadata'; import 'mocha'; import {expect} from 'chai'; import {PingFinder} from '../../../src/services/ping-finder'; import {MessageResponder} from '../../../src/services/message-responder'; import {instance, mock, verify, when} from 'ts-mockito'; import {Message} from 'discord.js'; describe('MessageResponder', () => { let mockedPingFinderClass: PingFinder; let mockedPingFinderInstance: PingFinder; let mockedMessageClass: Message; let mockedMessageInstance: Message; let service: MessageResponder; beforeEach(() => { mockedPingFinderClass = mock(PingFinder); mockedPingFinderInstance = instance(mockedPingFinderClass); mockedMessageClass = mock(Message); mockedMessageInstance = instance(mockedMessageClass); setMessageContents(); service = new MessageResponder(mockedPingFinderInstance); }) it('should reply', async () => { whenIsPingThenReturn(true); await service.handle(mockedMessageInstance); verify(mockedMessageClass.reply('pong!')).once(); }) it('should not reply', async () => { whenIsPingThenReturn(false); await service.handle(mockedMessageInstance).then(() => { // Successful promise is unexpected, so we fail the test expect.fail('Unexpected promise'); }).catch(() => { // Rejected promise is expected, so nothing happens here }); verify(mockedMessageClass.reply('pong!')).never(); }) function setMessageContents() { mockedMessageInstance.content = 'Non-empty string'; } function whenIsPingThenReturn(result: boolean) { when(mockedPingFinderClass.isPing('Non-empty string')).thenReturn(result); } });

Katsed PingFinder on üsna tühised, kuna pole sõltuvusi, mida mõnitada. Siin on näide testjuhtumist:

describe('PingFinder', () => { let service: PingFinder; beforeEach(() => { service = new PingFinder(); }) it('should find 'ping' in the string', () => { expect(service.isPing('ping')).to.be.true }) });

Integratsioonitestide loomine

Lisaks ühikutestidele saame kirjutada ka integreerumisteste. Peamine erinevus on see, et nende testide sõltuvusi ei mõnitata. Siiski on mõned sõltuvused, mida ei tohiks testida, näiteks välised API-ühendused. Sel juhul saame luua mõnitusi ja rebind konteinerisse, nii et selle asemel süstitakse pilk. Siin on näide, kuidas seda teha:

import container from '../../inversify.config'; import {TYPES} from '../../src/types'; // ... describe('Bot', () => { let discordMock: Client; let discordInstance: Client; let bot: Bot; beforeEach(() => { discordMock = mock(Client); discordInstance = instance(discordMock); container.rebind(TYPES.Client) .toConstantValue(discordInstance); bot = container.get(TYPES.Bot); }); // Test cases here });

See viib meid Discordi bot õpetuse lõppu. Palju õnne, sa ehitasid selle puhtalt, TypeScript ja DI olid algusest peale paigas! See TypeScripti sõltuvuse süstimise näide on muster, mille saate oma repertuaari lisada mis tahes projektiga kasutamiseks.

TypeScripti ja sõltuvuse süstimine: mitte ainult Discord Bot'i arendamiseks

Objekti orienteeritud maailma toomine TypeScript JavaScripti on suurepärane täiustus, hoolimata sellest, kas töötame esi- või tagakoodi koodiga. Ainuüksi tüüpide kasutamine võimaldab meil vältida paljusid vigu. Sõltuvuse süstimine TypeScripti abil sunnib JavaScripti põhisele arendusele veelgi rohkem objektorienteeritud parimaid tavasid.

Muidugi ei saa keele piirangute tõttu see kunagi olema nii lihtne ja loomulik kui staatiliselt kirjutatud keeltes. Kuid üks on kindel: TypeScript, ühikutestid ja sõltuvuse süstimine võimaldavad meil kirjutada loetavamat, vabamalt ühendatud ja hooldatavat koodi - olenemata sellest, millist rakendust me arendame.

Seotud: Looge rakendus WhatsApp Chatbot, mitte rakendus

Põhitõdesid mõistes

Miks peaksin kasutama sõltuvussüsti?

Kui soovite kirjutada puhtama koodi selles mõttes, et see on ühtselt testitav, hooldatavam ja vabalt ühendatud, peaksite kasutama sõltuvuse süstimise kujundusmustrit. Kasutades sõltuvussüstimist, on teil puhtama koodi retsept ilma ratast leiutamata.

Mis on sõltuvuse süstimise eelised?

Rakendades sõltuvuse süstimist, oleme sunnitud kirjutama ühikutestitava koodi, mida on lihtne hooldada. Sõltuvused süstitakse konstruktorite kaudu ja neid saab ühikutestide abil hõlpsasti mõnitada. Samuti julgustab see muster kirjutama lõdvalt seotud koodi.

Mis on TypeScripti eesmärk?

TypeScripti põhieesmärk on lubada puhtamat ja loetavamat JavaScripti koodi, lisades tüüpe. See on abi arendajatele, enamasti on see kasutusel IDE-des. Kapoti all teisendatakse TypeScript endiselt tavaliseks JavaScripti.

Mis on Discordi bot?

Discord bot on veebirakendus, mis kasutab suhtlemiseks Discord API-d.

Mida saab teha Discordi bot?

Discordi bot saab vastata sõnumitele, määrata rolle, vastata reaktsioonidega ja palju muud. Igale ebakõla toimingule, mida tavakasutajad ja administraatorid saavad teha, on API-meetodid.

Mis on TypeScripti eelised?

TypeScripti peamine eelis on lubada arendajal tüüpe määratleda ja kasutada. Tüübivihjete abil teab transpiler (või „allikast lähte juurde kompilaator“), milline objekt tuleks antud meetodile edastada. Võimalikud vead või kehtetud kõned tuvastatakse kompileerimise ajal, mis viib live-serveris vähem vigu.

Juhend oma esimese Ember.js rakenduse loomiseks

Veebi Kasutajaliides

Juhend oma esimese Ember.js rakenduse loomiseks
Juhend JUnitiga ühtsete ja integreeritud testide kohta

Juhend JUnitiga ühtsete ja integreeritud testide kohta

Tagumine Ots

Lemmik Postitused
Tasulise meedia direktor
Tasulise meedia direktor
10 parimat viga, mida Django arendajad teevad
10 parimat viga, mida Django arendajad teevad
Instagrami privaatsuspoliitika ülevaade
Instagrami privaatsuspoliitika ülevaade
Disainerite värviteooria - kokkupõrkekursus (koos infograafikaga)
Disainerite värviteooria - kokkupõrkekursus (koos infograafikaga)
Kliendi hoidmise tähtsus - empiiriline uuring
Kliendi hoidmise tähtsus - empiiriline uuring
 
Ettekande kujundus ja visuaalse jutustamise kunst
Ettekande kujundus ja visuaalse jutustamise kunst
Disainisüsteemide ja mustrite mõistmine
Disainisüsteemide ja mustrite mõistmine
Kuidas anda professionaalset disaini tagasisidet
Kuidas anda professionaalset disaini tagasisidet
Töötluskeele ülim juhend I osa: põhialused
Töötluskeele ülim juhend I osa: põhialused
Taiwani arendaja Hsiao Wei Chen võidab seitsmenda ApeeScape'i stipendiumi
Taiwani arendaja Hsiao Wei Chen võidab seitsmenda ApeeScape'i stipendiumi
Kategooriad
TrendidPlaneerimine Ja PrognoosimineTooteinimesed Ja MeeskonnadRedigeerimineMuuladustamineToote ElutsükkelInnovatsioonKpi-D Ja AnalyticsVeaotsing

© 2023 | Kõik Õigused Kaitstud

socialgekon.com