-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: performance test with k6 (#480)
- Loading branch information
Showing
24 changed files
with
723 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
screenshots | ||
logs | ||
./k6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Performance tests | ||
|
||
The tests are written in TypeScript and run by the k6 | ||
test tool (https://k6.io/docs/) within a browser context. | ||
|
||
### Run modes | ||
|
||
- Functional test: 1 user and 1 iteration | ||
- Performance test: X iterations over Y users | ||
|
||
See `scenario/scenario.ts` for details. | ||
|
||
### Commands | ||
|
||
Install k6 and the xk6-extension to write to file (error log in `/logs`). On Mac (assume Go is installed along with `GOPATH` is set on `PATH`): | ||
```bash | ||
$ brew install k6 | ||
$ go install go.k6.io/xk6/cmd/xk6@latest | ||
$ xk6 build v0.54.0 --with github.com/avitalique/xk6-file@latest | ||
$ yarn install | ||
``` | ||
|
||
Run functional test | ||
|
||
```bash | ||
# Headless | ||
e2e-tests/k6$ ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod] | ||
# With browser | ||
e2e-tests/k6$ K6_BROWSER_HEADLESS=false ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod] | ||
``` | ||
|
||
Run performance test - default: 100 iterations with 10 users | ||
|
||
```bash | ||
e2e-tests/k6$ ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod] -e performanceTest=true | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { env } from './env.ts'; | ||
import { funcOptions, perfOptions } from './options.ts'; | ||
import { Options } from 'k6/options'; | ||
import { functScenario, perfScenario } from '../scenario/scenario.ts'; | ||
import { Metrics } from '../measurements/metrics.ts'; | ||
|
||
class Conf { | ||
/* @ts-ignore */ | ||
host: string = __ENV.host || env.environments[__ENV.env || 'dev'].host; | ||
|
||
/* @ts-ignore */ | ||
options: Options = | ||
__ENV.performanceTest === 'true' ? perfOptions : funcOptions; | ||
|
||
/* @ts-ignore */ | ||
isPerformanceTest: boolean = __ENV.performanceTest === 'true'; | ||
|
||
/* @ts-ignore */ | ||
usecase = (metrics: Metrics): Promise<void> => { | ||
__ENV.performanceTest === 'true' | ||
? perfScenario(metrics) | ||
: functScenario(metrics); | ||
}; | ||
} | ||
|
||
// eslint-disable-next-line import/no-anonymous-default-export | ||
export default new Conf(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { EnvType } from '../types'; | ||
|
||
export const env: EnvType = { | ||
environments: { | ||
dev: { | ||
host: 'http://localhost:3000', | ||
}, | ||
staging: { | ||
host: 'https://atb-staging.planner-web.mittatb.no', | ||
}, | ||
prod: { | ||
host: 'https://atb.planner-web.mittatb.no', | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Options } from 'k6/options'; | ||
|
||
export const funcOptions: Options = { | ||
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'count'], | ||
scenarios: { | ||
ui: { | ||
executor: 'shared-iterations', | ||
vus: 1, | ||
iterations: 1, | ||
options: { | ||
browser: { | ||
type: 'chromium', | ||
}, | ||
}, | ||
}, | ||
}, | ||
thresholds: {}, | ||
}; | ||
|
||
export const perfOptions: Options = { | ||
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'count'], | ||
scenarios: { | ||
ui: { | ||
executor: 'shared-iterations', | ||
vus: 10, | ||
iterations: 100, | ||
options: { | ||
browser: { | ||
type: 'chromium', | ||
}, | ||
}, | ||
}, | ||
}, | ||
thresholds: {}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { FromLocationType, ToLocationType } from '../types'; | ||
|
||
export const getFromLocation = (): FromLocationType => { | ||
return fromLocations[Math.floor(Math.random() * fromLocations.length)]; | ||
}; | ||
|
||
export const getFromLocationName = (): string => { | ||
return fromLocations[Math.floor(Math.random() * fromLocations.length)].name; | ||
}; | ||
|
||
export const getToLocationName = (): string => { | ||
return toLocations[Math.floor(Math.random() * toLocations.length)].name; | ||
}; | ||
|
||
export const getToLocationRegionName = (): string => { | ||
return toLocationsRegion[Math.floor(Math.random() * toLocationsRegion.length)] | ||
.name; | ||
}; | ||
|
||
const fromLocations: FromLocationType[] = [ | ||
{ | ||
name: 'Olav Tryggvasons gate', | ||
quay: '1', | ||
}, | ||
{ | ||
name: 'Prinsens gate', | ||
quay: 'P1', | ||
}, | ||
{ | ||
name: 'Klettkrysset', | ||
quay: '1', | ||
}, | ||
{ | ||
name: 'Dronningens gate', | ||
quay: 'D1', | ||
}, | ||
{ | ||
name: 'Tillerterminalen', | ||
quay: '1', | ||
}, | ||
]; | ||
|
||
const toLocations: ToLocationType[] = [ | ||
{ | ||
name: 'Melhus sentrum', | ||
}, | ||
{ | ||
name: 'Stjørdal stasjon', | ||
}, | ||
{ | ||
name: 'Husebytunet', | ||
}, | ||
{ | ||
name: 'Lade idrettsanlegg', | ||
}, | ||
{ | ||
name: 'Ranheim idrettsplass', | ||
}, | ||
]; | ||
|
||
const toLocationsRegion: ToLocationType[] = [ | ||
{ | ||
name: 'Namsos skysstasjon', | ||
}, | ||
{ | ||
name: 'Meråker sentrum', | ||
}, | ||
{ | ||
name: 'Hitra idrettspark', | ||
}, | ||
{ | ||
name: 'Berkåk sentrum', | ||
}, | ||
{ | ||
name: 'Selbu skysstasjon', | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { Page } from 'k6/browser'; | ||
|
||
export class Measures { | ||
private page: Page; | ||
|
||
constructor(page: Page) { | ||
this.page = page; | ||
} | ||
|
||
async mark(label: string) { | ||
switch (label) { | ||
case 'search': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('search'); | ||
}); | ||
break; | ||
case 'search-firstResult': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('search-firstResult'); | ||
}); | ||
break; | ||
case 'search-lastResult': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('search-lastResult'); | ||
}); | ||
break; | ||
case 'assistant-details-open': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('assistant-details-open'); | ||
}); | ||
break; | ||
case 'assistant-details-opened': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('assistant-details-opened'); | ||
}); | ||
break; | ||
case 'departures-details-open': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('departures-details-open'); | ||
}); | ||
break; | ||
case 'departures-details-opened': | ||
await this.page.evaluate(() => { | ||
window.performance.mark('departures-details-opened'); | ||
}); | ||
break; | ||
} | ||
} | ||
|
||
async measure(label: string) { | ||
switch (label) { | ||
case 'measure-search-firstResult': | ||
await this.page.evaluate(() => | ||
window.performance.measure( | ||
'measure-search-firstResult', | ||
'search', | ||
'search-firstResult', | ||
), | ||
); | ||
return await this.page.evaluate( | ||
() => | ||
JSON.parse( | ||
JSON.stringify( | ||
window.performance.getEntriesByName( | ||
'measure-search-firstResult', | ||
), | ||
), | ||
)[0].duration, | ||
); | ||
case 'measure-search-lastResult': | ||
await this.page.evaluate(() => | ||
window.performance.measure( | ||
'measure-search-lastResult', | ||
'search', | ||
'search-lastResult', | ||
), | ||
); | ||
return await this.page.evaluate( | ||
() => | ||
JSON.parse( | ||
JSON.stringify( | ||
window.performance.getEntriesByName( | ||
'measure-search-lastResult', | ||
), | ||
), | ||
)[0].duration, | ||
); | ||
case 'measure-assistant-details-open': | ||
await this.page.evaluate(() => | ||
window.performance.measure( | ||
'measure-assistant-details-open', | ||
'assistant-details-open', | ||
'assistant-details-opened', | ||
), | ||
); | ||
return await this.page.evaluate( | ||
() => | ||
JSON.parse( | ||
JSON.stringify( | ||
window.performance.getEntriesByName( | ||
'measure-assistant-details-open', | ||
), | ||
), | ||
)[0].duration, | ||
); | ||
case 'measure-departures-details-open': | ||
await this.page.evaluate(() => | ||
window.performance.measure( | ||
'measure-departures-details-open', | ||
'departures-details-open', | ||
'departures-details-opened', | ||
), | ||
); | ||
return await this.page.evaluate( | ||
() => | ||
JSON.parse( | ||
JSON.stringify( | ||
window.performance.getEntriesByName( | ||
'measure-departures-details-open', | ||
), | ||
), | ||
)[0].duration, | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.