The Codest
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Odvětví
    • Fintech a bankovnictví
    • E-commerce
    • Adtech
    • Healthtech
    • Výroba
    • Logistika
    • Automobilový průmysl
    • IOT
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
  • O nás
  • Služby
    • Vývoj softwaru
      • Vývoj frontendů
      • Vývoj backendu
    • Staff Augmentation
      • Vývojáři frontendů
      • Vývojáři backendu
      • Datoví inženýři
      • Cloudoví inženýři
      • Inženýři QA
      • Další
    • To Advisory
      • Audit a poradenství
  • Hodnota za
    • CEO
    • CTO
    • Manažer dodávek
  • Náš tým
  • Case Studies
  • Vědět jak
    • Blog
    • Setkání
    • Webové semináře
    • Zdroje
Kariéra Spojte se s námi
Šipka zpět ZPĚT
2019-02-04
Vývoj softwaru

TESTOVÁNÍ JAVASCRIPTU... POMOCÍ RUBY?!

Pawel Wal

Ačkoli je Codest především Ruby shop, jeden z mnoha projektů, které stavíme, je v JavaScript. Jedná se o knihovnu na straně klienta, která běží v poměrně náročném prostředí: musí podporovat téměř všechny existující prohlížeče, včetně těch velmi starých, a navíc spolupracuje s množstvím externích skriptů a služeb. Je to spousta zábavy.

Zvláštní případ nesvazkových závislostí

S výše uvedenými požadavky se pojí celá řada problémů, které se u klientských systémů obvykle nevyskytují. projekta jedna z těchto otázek se týká testování. Samozřejmě máme bezvadnou sadu jednotkových testů a v našem prostředí CI/CD je spouštíme proti velmi rozsáhlé matici kombinací prohlížečů a operačních systémů, ale to samo o sobě nezkoumá vše, co se může pokazit.

Vzhledem k zastřešující architektuře ekosystému, ve kterém pracujeme, jsme závislí na tom, že některé externí knihovny jsou načteny společně s našimi - to znamená, že je nepřibalujeme k našim knihovnám. kód; nemůžeme a nedá se s tím nic dělat. To představuje zajímavou výzvu, protože tyto knihovny:

  • možná ani nebude - pokud někdo pokazí implementaci naší knihovny,
  • mohou být k dispozici, ale v nesprávných/nekompatibilních verzích,
  • mohl být upraven jiným kódem, který je v konkrétní implementaci součástí systému.

To jasně ukazuje, proč nestačí jednotkové testy: testují izolovaně od reálného světa. Řekněme, že na základě toho, co jsme zjistili v dokumentaci, vytvoříme maketu nějaké části veřejného API externí knihovny a spustíme proti ní jednotkový test. Co to dokazuje?

Možná budete v pokušení říct "to znamená, že to funguje s rozhraním API externí knihovny", ale to se bohužel mýlíte. Znamená to pouze, že správně spolupracuje s podmnožinou veřejného API externí knihovny, a to ještě pouze s verzí, kterou jsme si vymodelovali.

Co když se knihovna doslova změní zpod nás? Co když - ve volné přírodě - dostane nějaké podivné reakce, které ho donutí zasáhnout jinou, nedokumentovanou cestu kódu? Můžeme se proti tomu vůbec chránit?

Přiměřená ochrana

Ne 100%, ne - na to je prostředí příliš složité. Ale můžeme si být dostatečně jisti, že vše funguje tak, jak má, pomocí několika zobecněných příkladů toho, co by se s naším kódem mohlo stát v přírodě: můžeme provést integrační testování. Jednotkové testy zajišťují, že náš kód běží správně interně, a integrační testy musí zajistit, že si správně "povídáme" s knihovnami, které nemůžeme ovládat. A to ani s jejich odnoží - se skutečnými, živými knihovnami.

Mohli bychom použít některý z dostupných integračních testovacích rámců pro JavaScript, vytvořte jednoduchou stránku HTML, předhoďte na ni několik volání naší knihovny a vzdálených knihoven a pořádně ji procvičte. Nechceme však zahltit žádný z koncových bodů vzdálených služeb voláními generovanými našimi prostředími CI/CD. To by nám rozhodilo některé statistiky, možná by to něco rozbilo a - v neposlední řadě - nebylo by moc hezké, kdybychom z něčí produkce udělali součást našich testů.

Bylo ale vůbec možné něco tak složitého testovat? Protože Ruby je naší první a hlavní láskou, vrátili jsme se k našim odborným znalostem a začali přemýšlet o tom, jak obvykle provádíme integrační testování se vzdálenými službami v projektech Ruby. Mohli bychom použít něco jako např. vcr klenot, aby jednou zaznamenal, co se děje, a pak to v případě potřeby přehrával našim testům.

Zadejte proxy

Vnitřně toho vcr dosahuje prostřednictvím proxy požadavků. To byl náš a-ha! moment. Potřebovali jsme proxy server pro každý požadavek, který by neměl zasáhnout nic na "skutečném" internetu, na nějaké stubbed odpovědi. Pak se tato příchozí data předají externí knihovně a náš kód běží jako obvykle.

Když vytváříme prototyp něčeho, co se zdá být složité, často se vracíme k jazyku Ruby jako k metodě, která šetří čas. Rozhodli jsme se vytvořit prototyp testovací svazek pro náš JavaScript v jazyce Ruby, abychom zjistili, jak dobře bude myšlenka proxy fungovat, než se pustíme do vytváření něčeho složitějšího v (možná) JavaScript. Ukázalo se, že je to překvapivě jednoduché. Vlastně je to tak jednoduché, že si ho v tomto článku společně postavíme 🙂.

Světla, kamera... počkat, zapomněli jsme na rekvizity!

Samozřejmě se nebudeme zabývat "skutečnými věcmi" - vysvětlení byť jen části toho, co stavíme, je daleko za hranicemi rozsahu příspěvku na blogu. Můžeme vytvořit něco rychlého a jednoduchého, co bude stát v pozadí daných knihoven, a pak se více soustředit na část týkající se Ruby.

Nejdříve potřebujeme něco, co bude zastupovat externí knihovnu, se kterou pracujeme. Potřebujeme, aby vykazovala několik chování: měla by kontaktovat externí službu, tu a tam vyslat nějakou událost a především - neměla by být vytvořena s ohledem na snadnou integraci 🙂.

Použijeme tyto informace:

/* global XMLHttpRequest, Event */

const URL = 'https://poloniex.com/public/?command=returnTicker'
const METHOD = 'GET'

module.exports = {
fetch: function () {
var req = new XMLHttpRequest()
req.responseType = 'json'
req.open(METHOD, URL, true)
var doneEvent = new Event('example:fetched')

req.onreadystatechange = function (aEvt) {
  if (req.readyState === 4) {
    if (req.status === 200) {
      this.data = req.response
    } else {
      this.error = true
    }
    window.dispatchEvent(doneEvent)
  }
}.bind(this)

req.send(null)

},
error: false,
data: {}
}

Všimněte si, že volá otevřené rozhraní API pro některá data - v tomto případě pro kurzy kryptoměn, protože to je v současné době v módě Toto rozhraní API nevystavuje sandbox a je omezeno rychlostí, což z něj dělá ukázkový příklad něčeho, co by se v testech nemělo používat.

Možná jste si všimli, že se ve skutečnosti jedná o modul kompatibilní s NPM, zatímco jsem naznačil, že skript, kterým se obvykle zabýváme, není k dispozici v NPM pro snadné sdružování. Pro tuto ukázku stačí, že vykazuje určité chování, a raději bych zde měl snadné vysvětlení za cenu přílišného zjednodušení.

Pozvání herců

Nyní potřebujeme také něco, co by zastoupilo naši knihovnu. Požadavky budou opět jednoduché: musí to volat naši "externí" knihovnu a něco dělat s výstupem. V zájmu zachování jednoduchosti "testovatelné" části ji také necháme provádět dvojí logování: jednak do konzole, což je ve specifikacích trochu hůře čitelné, jednak do globálně dostupného pole.

window.remote = require('remote-calling-example')

window.failedMiserably = true
window.logs = []

function log (zpráva) {
window.logs.push(zpráva)
console.log(zpráva)
}

window.addEventListener('example:fetched', function () {
if (window.remote.error) {
log('[EXAMPLE] Remote fetch failed')
window.failedMiserably = true
} else {
log('[EXAMPLE] Remote fetch successful')
log([EXAMPLE] BTC to ETH: ${okno.remote.data.BTC_ETH.last})
}
})

window.remote.fetch()

Záměrně také udržuji chování ohromně jednoduché. Už takhle jsou tu jen dvě skutečně zajímavé cesty kódu, které je třeba specifikovat, takže nás při postupu sestavování nezavalí lavina specifikací.

Všechno to do sebe zapadá

Vytvoříme jednoduchou stránku HTML:

<code> <!DOCTYPE html>
 <html>
 <head>
   <title>Příklad stránky</title>
   <script type="text/javascript" src="./index.js"></script>
 </head>
 <body></body>
 </html>

Pro účely této ukázky spojíme naše HTML a JavaScript dohromady s. Parcela, velmi jednoduchý webová aplikace bundler. Parcel se mi hodně líbí v takových chvílích, kdy dávám dohromady rychlý příklad nebo se snažím nahodit nápad na třídu. Když děláte něco tak jednoduchého, že by konfigurace Webpacku trvala déle než psaní požadovaného kódu, je to nejlepší.

Je také natolik nenápadný, že když chci přejít na něco, co je trochu více vyzkoušené, nemusím z Parcelu téměř vůbec couvat, což se o Webpacku říct nedá. Pozor však - Parcel je ve fázi intenzivního vývoje a problémy se mohou objevit a objeví; měl jsem problém, kdy transpilovaný výstup JavaScript byl neplatný na starším počítači. Node.js. Sečteno a podtrženo: zatím ji nezařazujte do svého výrobního procesu, ale přesto ji vyzkoušejte.

Využití síly integrace

Nyní můžeme sestavit náš testovací svazek.

Pro samotný rámec specifikace jsme použili rspec. Ve vývojových prostředích testujeme pomocí skutečného prohlížeče Chrome bez hlavy - úkol spustit a kontrolovat tento prohlížeč připadl na. watir (a jeho věrný pomocník watir-rspec). Pro našeho zástupce jsme pozvali Puffing Billy a stojan na večírek. Nakonec chceme při každém spuštění specifikací znovu spustit sestavení JavaScript, čehož dosáhneme pomocí příkazu kokain.

To je celá řada pohyblivých částí, takže náš pomocník pro specifikace je i v tomto jednoduchém příkladu poněkud... zapojen. Podívejme se na něj a rozeberme ho.

Dir['./spec/support/*/.rb'].each { |f| require f }

TEST_LOGGER = Logger.new(STDOUT)

RSpec.configure do |config|
config.before(:suite) { Cocaine::CommandLine.new('npm', 'run build', logger: TEST_LOGGER).run }

config.include Watir::RSpec::Helper
config.include Watir::RSpec::Matchers

config.include ProxySupport

config.order = :random
BrowserSupport.configure(config)
konec

Billy.configure do |c|
c.cache = false
c.cacherequestheaders = false
c.persistcache = false
c.recordstubrequests = true
c.logger = Logger.new(File.expandpath('../log/billy.log', FILE))
end

Před celou sadou spustíme náš vlastní příkaz pro sestavení pomocí programu Cocaine. Ta konstanta TEST_LOGGER je možná trochu přehnaná, ale nás zde počet objektů příliš nezajímá. Spouštíme samozřejmě specifikace v náhodném pořadí a potřebujeme zahrnout všechny dobroty z watir-rspec. Musíme také nastavit Billy tak, aby neprovádělo žádné ukládání do mezipaměti, ale rozsáhlé logování do spec/log/billy.log. Pokud nevíte, zda se požadavek skutečně stubbuje, nebo zda se dostává na živý server (jéje!), je tento protokol naprostým zlatem.

Jsem si jistý, že vaše bystré oči si již všimly ProxySupport a BrowserSupport. Možná si říkáte, že tam sedí naše vlastní dobroty... a máte naprostou pravdu! Podívejme se nejprve, co dělá BrowserSupport.

Prohlížeč, řízený

Nejprve si představíme TempBrowser:

třída TempBrowser
def get
@browser ||= Watir::Browser.new(web_driver)
end

def kill
@browser.close if @browser
@browser = nil
end

private

def web_driver
Selenium::WebDriver.for(:chrome, options: options)
end

def options
Selenium::WebDriver::Chrome::Options.new.tap do |options|
options.addargument '--auto-open-devtools-for-tabs'
options.addargument "--proxy-server=#{Billy.proxy.host}:#{Billy.proxy.port}"
konec
end
end

Když projdeme stromem volání zpět, zjistíme, že nastavujeme sadu možností prohlížeče Selenium pro Chrome. Jedna z voleb, kterou do ní předáváme, je pro naše nastavení klíčová: dává instanci Chrome pokyn, aby vše proxyovala přes naši instanci Puffing Billy. Druhá možnost je jen příjemná - každá instance, kterou spustíme a která není bezhlavý se automaticky otevřou kontrolní nástroje. To nám ušetří nespočetné množství Cmd+Alt+I za den 😉.

Poté, co prohlížeč nastavíme pomocí těchto možností, předáme jej společnosti Watir a to je v podstatě vše. Stránka zabít metoda je tak trochu cukr, který nám umožňuje opakovaně zastavit a znovu spustit ovladač, pokud potřebujeme, aniž bychom museli zahodit instanci TempBrowseru.

Nyní můžeme dát našim příkladům rspec několik superschopností. Především získáme šikovnou funkci prohlížeč pomocné metody, kolem které se budou naše specifikace převážně točit. Můžeme také využít šikovnou metodu pro restartování prohlížeče pro konkrétní příklad, pokud děláme něco supercitlivého. Samozřejmě také chceme prohlížeč po dokončení testovací sady zabít, protože v žádném případě nechceme, aby v něm zůstávaly instance Chrome - kvůli naší paměti RAM.

modul BrowserSupport
def self.browser
@browser ||= TempBrowser.new
konec

def self.configure(config)
config.around(:each) do |example|
BrowserSupport.browser.kill if example.metadata[:clean]
@browser = BrowserSupport.browser.get
@browser.cookies.clear
@browser.driver.manage.timeouts.implicit_wait = 30
example.run
end

config.after(:suite) do
  BrowserSupport.browser.kill
end

end
end

Zapojení proxy serveru

Máme nastavený prohlížeč a pomocníky specifikací a jsme připraveni začít odesílat požadavky na náš proxy server. Ale počkejte, ještě jsme ho nenastavili! Mohli bychom nabouchat opakované volání Billyho pro každý příklad, ale je lepší si pořídit pár pomocných metod a ušetřit si pár tisíc stisků kláves. To je to, co ProxySupport dělá.

Ten, který používáme v našem testovacím nastavení, je o něco složitější, ale zde je obecná představa:

frozenstringliteral: true

require 'json'

modul ProxySupport
HEADERS = {
'Access-Control-Allow-Methods' => 'GET',
'Access-Control-Allow-Headers' => 'X-Requested-With, X-Prototype-Version, Content-Type',
'Access-Control-Allow-Origin' => '*'
}.freeze

def stubjson(url, file)
Billy.proxy.stub(url).andreturn({
body: open(file).read,
code: 200,
headers: HEADERS.dup
})
konec

def stubstatus(url, status)
Billy.proxy.stub(url).andreturn({
body: '',
code: status,
headers: HEADERS.dup
})
konec

def stubpage(url, file)
Billy.proxy.stub(url).andreturn(
body: open(file).read,
content_type: 'text/html',
code: 200
)
konec

def stubjs(url, file)
Billy.proxy.stub(url).andreturn(
body: open(file).read,
content_type: 'application/javascript',
code: 200
)
konec
konec

Můžeme si stoupnout:

  • Požadavky na stránku HTML - pro naši hlavní stránku "hřiště",
  • Požadavky JS - k obsluze naší svazkové knihovny,
  • Požadavky JSON - pro zadání požadavku na vzdálené rozhraní API,
  • a požadavek "cokoli", u kterého nás zajímá pouze vrácení konkrétní odpovědi HTTP, která není 200.

Pro náš jednoduchý příklad to bude stačit. Když už mluvíme o příkladech - měli bychom jich pár vytvořit!

Testování dobré stránky

Nejprve musíme pro náš proxy server vytvořit několik "tras":

let(:pageurl) { 'http://myfancypage.local/index.html' }
let(:jsurl) { 'http://myfancypage.local/dist/remote-caller-example.js' }

let(:pagepath) { './dist/index.html' }
let(:jspath) { './dist/remote-caller-example.js' }

before do
stubpage pageurl, pagepath
stubjs jsurl, jspath
end

Stojí za zmínku, že z pohledu rspec se zde relativní cesty vztahují k hlavnímu adresáři projektu, takže načítáme naše HTML a JS přímo z adresáře. dist adresář - jak jej sestavil Parcel. Již nyní můžete vidět, jak tyto stub_* se hodí pomocníci.

Za zmínku stojí také to, že naše "falešné" webové stránky umisťujeme na. .local TLD. Tímto způsobem by nemělo dojít k úniku požadavků z našeho místního prostředí, pokud by se něco pokazilo. Jako obecnou praxi bych doporučoval alespoň nepoužívat "skutečná" doménová jména ve stubs, pokud to není nezbytně nutné.

Další poznámka, kterou bychom zde měli uvést, se týká toho, abychom se neopakovali. Jak se směrování proxy serverů stává složitějším, s mnohem více cestami a adresami URL, bude mít skutečnou hodnotu extrahovat toto nastavení do sdíleného kontextu a jednoduše ho zahrnout podle potřeby.

Nyní můžeme specifikovat, jak by měla vypadat naše "dobrá" cesta:

context 'se správnou odpovědí' do
before do
stubjson %r{http://poloniex.com/public(.*)}, './spec/fixtures/remote.json'
goto pageurl
Watir::Wait.until { browser.execute_script('return window.logs.length === 2') }
end

it 'logs proper data' do
expect(browser.execute_script('return window.logs')).to(
eq(['[EXAMPLE] Remote fetch successful', '[EXAMPLE] BTC to ETH: 0.03619999'])
)
end
end

To je docela jednoduché, že? Ještě trochu nastavení - odpověď JSON ze vzdáleného rozhraní API přerušíme pomocí fixu, přejdeme na naši hlavní adresu URL a pak... čekáme.

Nejdelší čekání

Čekání je způsob, jak obejít omezení, na které jsme narazili u systému Watir - nemůžeme spolehlivě čekat např. na události JavaScript, takže musíme trochu podvádět a "čekat", až skripty přesunou nějaký objekt, ke kterému máme přístup, do stavu, který nás zajímá. Nevýhodou je, že pokud tento stav nikdy nenastane (např. kvůli chybě), musíme čekat, až watir waiter vyprší. To trochu prodlužuje čas specifikace. Specifikace však stále spolehlivě selhává.

Poté, co se stránka "ustálí" na stavu, který nás zajímá, můžeme v kontextu stránky provést další JavaScript. Zde vyvoláme protokoly zapsané do veřejného pole a zkontrolujeme, zda jsou takové, jaké jsme očekávali.

Poznámka na okraj - v tomto případě se vzdálený požadavek opravdu vyplatí. Odpověď, která se zaznamená do konzoly, je závislá na kurzu vráceném vzdáleným rozhraním API, takže bychom nemohli spolehlivě otestovat obsah protokolu, kdyby se neustále měnil. Existují samozřejmě způsoby, jak to obejít, ale nejsou příliš elegantní.

Testování špatné větve

Ještě jedna věc k otestování: větev "selhání".

context 's neúspěšnou odpovědí' do
before do
stubstatus %r{http://poloniex.com/public(.*)}, 404
goto pageurl
Watir::Wait.until { browser.execute_script('return window.logs.length === 1') }
end

it 'logs failure' do
expect(browser.execute_script('return window.logs')).to(
eq(['[EXAMPLE] Remote fetch failed'])
)
end
end

Je to velmi podobné výše uvedenému, s tím rozdílem, že odpověď vrátí stavový kód HTTP 404 a očekáváme jiný protokol.

Spusťme nyní naše specifikace.

% balíček exec rspec
Randomizováno s osivem 63792
I, [2017-12-21T14:26:08.680953 #7303] INFO -- : Příkaz :: npm run build

Vzdálené volání
se správnou odpovědí
zaznamenává správná data
s chybnou odpovědí
zaznamená selhání

Dokončeno za 23,56 sekundy (načtení souborů trvalo 0,86547 sekundy)
2 příklady, 0 selhání

Woohoo!

Závěr

Stručně jsme probrali, jak lze JavaScript integrovat s jazykem Ruby. I když jsme to původně považovali spíše za provizorium, nyní jsme s naším malým prototypem docela spokojeni. Stále samozřejmě uvažujeme o čistém řešení JavaScript, ale zatím máme jednoduchý a praktický způsob, jak reprodukovat a testovat některé velmi složité situace, se kterými jsme se setkali ve volné přírodě.

Pokud uvažujete o tom, že si něco podobného postavíte sami, je třeba poznamenat, že to není bez omezení. Například pokud bude testovaný obsah opravdu náročný na AJAX, bude Puffing Billy dlouho reagovat. Také pokud budete muset stubovat některé zdroje SSL, bude zapotřebí trochu více práce - pokud je to váš požadavek, podívejte se do dokumentace watiru. Určitě budeme pokračovat ve zkoumání a hledání nejlepších způsobů, jak se vypořádat s naším jedinečným případem použití - a určitě vás také budeme informovat o tom, na co jsme přišli.

Související články

Ilustrace zdravotnické aplikace pro chytré telefony s ikonou srdce a rostoucím zdravotním grafem, označená logem The Codest, která představuje digitální zdraví a řešení HealthTech.
Vývoj softwaru

Softwarové vybavení pro zdravotnictví: a případy použití

Nástroje, na které se dnes zdravotnické organizace spoléhají, se v ničem nepodobají papírovým kartám z doby před desítkami let. zdravotnický software dnes podporuje zdravotnické systémy, péči o pacienty a moderní poskytování zdravotní péče v klinických a...

NEJKRÁSNĚJŠÍ
Abstraktní ilustrace klesajícího sloupcového grafu se stoupající šipkou a zlatou mincí symbolizující efektivitu nákladů nebo úspory. V levém horním rohu se zobrazuje logo The Codest se sloganem "In Code We Trust" na světle šedém pozadí.
Vývoj softwaru

Jak rozšířit tým vývojářů bez ztráty kvality produktu

Zvětšujete svůj vývojový tým? Zjistěte, jak růst, aniž byste museli obětovat kvalitu produktu. Tento průvodce se zabývá příznaky, že je čas na škálování, strukturou týmu, najímáním zaměstnanců, vedením a nástroji - a také tím, jak může The Codest...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

Vytváření webových aplikací odolných vůči budoucnosti: postřehy týmu odborníků The Codest

Zjistěte, jak společnost The Codest vyniká při vytváření škálovatelných, interaktivních webových aplikací pomocí nejmodernějších technologií, které poskytují bezproblémové uživatelské prostředí na všech platformách. Zjistěte, jak naše odborné znalosti podporují digitální transformaci a obchodní...

NEJKRÁSNĚJŠÍ
Vývoj softwaru

10 nejlepších lotyšských společností zabývajících se vývojem softwaru

V našem nejnovějším článku se dozvíte o nejlepších lotyšských společnostech zabývajících se vývojem softwaru a jejich inovativních řešeních. Zjistěte, jak mohou tito technologičtí lídři pomoci pozvednout vaše podnikání.

thecodest
Podniková a škálovací řešení

Základy vývoje softwaru v jazyce Java: A Guide to Outsourcing Successfully

Prozkoumejte tuto základní příručku o úspěšném vývoji softwaru outsourcing Java, abyste zvýšili efektivitu, získali přístup k odborným znalostem a dosáhli úspěchu projektu s The Codest.

thecodest

Přihlaste se k odběru naší znalostní databáze a získejte aktuální informace o odborných znalostech z oblasti IT.

    O nás

    The Codest - Mezinárodní společnost zabývající se vývojem softwaru s technologickými centry v Polsku.

    Spojené království - ústředí

    • Kancelář 303B, 182-184 High Street North E6 2JA
      Londýn, Anglie

    Polsko - Místní technologická centra

    • Kancelářský park Fabryczna, Aleja
      Pokoju 18, 31-564 Krakov
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polsko

      The Codest

    • Home
    • O nás
    • Služby
    • Case Studies
    • Vědět jak
    • Kariéra
    • Slovník

      Služby

    • To Advisory
    • Vývoj softwaru
    • Vývoj backendu
    • Vývoj frontendů
    • Staff Augmentation
    • Vývojáři backendu
    • Cloudoví inženýři
    • Datoví inženýři
    • Další
    • Inženýři QA

      Zdroje

    • Fakta a mýty o spolupráci s externím partnerem pro vývoj softwaru
    • Z USA do Evropy: Proč se americké startupy rozhodly přesídlit do Evropy?
    • Srovnání technických vývojových center v zahraničí: Tech Offshore Evropa (Polsko), ASEAN (Filipíny), Eurasie (Turecko)
    • Jaké jsou hlavní výzvy CTO a CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2026 by The Codest. Všechna práva vyhrazena.

    cs_CZCzech
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech