Transcript
Teste die Datenbank !
Thomas Koch Senior Database Specialist think project! International GmbH & Co. KG
Teste die Datenbank ! Warum? Womit? Wann? Wo? Was? Wie?
Warum ist die Datenbank zu testen? ●
Die Datenbank –
ist das Fundament eines jeden Systems
–
ist eine Komponente in jeder Systemlandschaft
–
spiegelt das Geschäftsmodell wieder
–
enthält Geschäftslogik
–
…
Vortrag:
Teste die Datenbank ! 4/26 Thomas Koch think project! International GmbH & Co. KG
Beispiel V-Modell
Wann bzw. Wo finden die DB-Tests statt?
Quelle: http://de.wikipedia.org/wiki/Datei:V-Modell.svg
Vortrag:
Teste die Datenbank ! 5/26 Thomas Koch think project! International GmbH & Co. KG
Definition Teststufen Unit Test ●
●
●
●
Auch Komponententest, Modultest
Integrationstest ●
Ein Test auf der Ebene der einzelnen Module der Software Testgegenstand ist die Funktionalität innerhalb einzelner abgrenzbarer Teile der Software –
Module
–
Programme oder Unterprogramme
–
Units oder Klassen
Testziel ist der Nachweis der technischen Lauffähigkeit und korrekter fachlicher (Teil-) Ergebnisse
●
●
testet die Zusammenarbeit voneinander abhängiger Komponenten Testschwerpunkt liegt auf den Schnittstellen der beteiligten Komponenten Nachweis korrekte Ergebnisse über komplette Abläufe hinweg
Quelle: http://de.wikipedia.org/wiki/Softwaretest
Wo finden die Datenbank-Tests statt?
Teststufen
Quelle: http://www.agilecoachjournal.com/wp-content/uploads/2014/01/AgileTestingPyramid2.jpg Vortrag:
Teste die Datenbank ! 7/26 Thomas Koch think project! International GmbH & Co. KG
● ● ● ● ● ●
JUnit PHPUnit PerlUnit TAP NUnit JSUnit
Komponte B Unit Test
Komponte B
Frontend
Unit Test
Unit Test
● ● ● ● ● ●
● ● ● ● ● ●
JUnit PHPUnit PerlUnit TAP NUnit JSUnit JUnit Arquillian PHPUnit PerlUnit TAP NUnit
Komponte B
Komponte B
Frontend
Unit Test
Unit Test
Unit Test
Komponte A
Komponte B
Integration Test
Komponte B Integration Test
Frontend
● ● ● ● ● ●
● ● ● ● ● ●
JUnit PHPUnit PerlUnit TAP NUnit JSUnit JUnit Arquillian PHPUnit PerlUnit TAP NUnit
Komponte B
Komponte B
Frontend
Unit Test
Unit Test
Unit Test
Komponte A
Komponte B
Integration Test
Komponte B
Frontend
Integration Test
● ●
Selenium Fit
Komponte B
Komponte B Akzeptanztest
Frontend
● ● ● ● ● ●
● ● ●
tSQLt pgTAP PGUnit myTAP DbFit PL/Unit DBUnit Test::DBUnit DBUnit.Net
Datenbank
Komponte B
Frontend
Unit Test Test Unit
Unit Test
Unit Test
Datenbank
Komponte B
Integration Test
Komponte B
Frontend
Integration Test
● ●
Selenium Fit
Datenbank
Komponte B Akzeptanztest
Frontend
Was ist bei der Datenbank zu testen? Datenbankstruktur ●
Existieren alle DBObjekte – –
Geschäftslogik ●
Stored Procedures
●
Lasttests
●
Trigger
●
Laufzeiten von
●
Constraints
Tabellen Views
–
Stores Procedures
–
Unique Keys
–
DML-Operationen
–
Check
–
Stores Procedures
–
Constraints
–
Sequenzen
●
Sequenzen
–
Operatoren
●
Views
–
…
●
Operatoren
●
User/Rollen → Privilegien
Views
Foreign Keys
Indexes
Datenbankeigenschaften
–
–
–
●
Performance
Vortrag:
Teste die Datenbank ! 12/26 Thomas Koch think project! International GmbH & Co. KG
Womit ist die PostgreSQL zu testen?
Umgebung
Tools
Datenbankstruktur
Geschäftslogik
Performance
●
check_postgres
●
pgUnit
●
JMeter
●
pgTap
●
pgTap
●
pgTap
●
liquibase
●
SoapUI
Auf allen ● ● ● ●
LIVE, TEST, DEV, ...
Nie auf LIVE Auf allen anderen ● ● ●
TEST DEV ...
● ● ● ●
Vortrag:
LIVE ? TEST DEV ? ...
Teste die Datenbank ! 13/26 Thomas Koch think project! International GmbH & Co. KG
pgTap „pgTAP is a unit testing framework for PostgreSQL written in PL/pgSQL and PL/SQL. It includes a comprehensive collection of TAP-emitting assertion functions, as well as the ability to integrate with other TAP-emitting test frameworks. It can also be used in the xUnit testing style.“ (www.pgtap.org) ●
Download http://pgxn.org/dist/pgtap/
●
Install database objects
●
–
CREATE EXTENSION pgtap; -- ab 9.1
–
psql -d mydb -f pgtap.sql
-- vor 9.1
873 functions 2 views
Start with pg_prove (search.cpan.org) Vortrag:
Teste die Datenbank ! 14/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Datenbankstruktur zu testen? Database
Owner/Privileges
PL/SQL Code
●
has_tablespace
●
db_owner_is
●
has_function
●
has_schema
●
schema_owner_is
●
has_cast
●
has_type
●
table_owner_is
●
has_operator
●
has_domain
●
database_priv_are
●
is_aggregate
●
domain_type_is
●
table_priv_are
●
is_strict
●
has_enum
●
function_priv_are
●
function_return
●
has_language
●
●
function_lang_is
●
trigger_is
●
●
is_superuser / isnt_superuser is_member_of has_user / has_role
Quelle: http://pgtap.org/documentation.html#theschemathings Vortrag:
Teste die Datenbank ! 15/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Datenbankstruktur zu testen? Table ●
●
●
●
has_table has_column / columns_are col_is_null / col_not_null col_has_default / col_hasnt_default
●
col_is_pk
●
col_is_fk
●
col_has_check
Index / Constraint
Other Objects
●
has_fk
●
has_sequence
●
fk_ok
●
has_view
●
has_unique
●
●
has_check ●
●
has_index
Quelle: http://pgtap.org/documentation.html#theschemathings Vortrag:
has_materialized_ view has_trigger
Teste die Datenbank ! 16/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Datenbankstruktur zu testen? SELECT has_schema('demo'); SELECT has_table('demo', 'tablexy', 'table public.tablexy exists'); SELECT has_sequence('demo', 'seq_tablexy_id', 'Sequence demo.seq_tablexy_id exists'); SELECT has_column('demo', 'tablexy', 'last_change_dt', 'Column demo.tablexy.last_change_dt exists'); SELECT col_type_is('demo', 'tablexy', 'id', 'bigint', 'Type of column demo.tablexy.id is bigint'); SELECT col_is_fk('demo', 'tablexy', ARRAY[ 'fk_id' ], 'FOREIGN KEY on column tablexy.fk_id exists'); SELECT col_is_pk('demo', 'tablexy', ARRAY[ 'id' ], 'PRIMARY KEY on columnn tablexy.id exists'); SELECT col_default_is('demo', 'tablexy', 'id','nextval(''demo.seq_tablexy_id''::regclass)', 'Column demo.tablexy.id has a default value: nextval(''demo.seq_tablexy_id''::regclass)'); SELECT has_trigger('demo', 'tablexy', 'triu_tablexy', 'INSERT/UPDATE Trigger demo.triu_tablexy on table tablexy should exists'); SELECT trigger_is('demo', 'tablexy', 'triu_tablexy', 'demo', 'tr_fnc', 'Trigger demo.triu_tablexy use function demo.tr_fnc()'); SELECT index_is_type('demo', 'tablexy', 'ix_tablexy_cols', 'btree', 'Index type is btree(demo.tablexy.ix_tablexy_cols)'); SELECT has_index('demo', 'tablexy', 'ix_tablexy_cols', ARRAY[ 'col1','col2'], 'Index demo.ix_tablexy_cols should exists');
Vortrag:
Teste die Datenbank ! 17/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Datenbankstruktur zu testen? SELECT ok('{}'::json = '{}'::json, 'check operator =(json,json)'); SELECT has_view('demo', 'v_xyz', 'View demo.v_xyz should exists'); SELECT has_type('demo', 'enumxy_type', 'Type demo.enumxy_type exists'); SELECT has_function('demo', 'fnc_xy', 'Function demo.fnc_xy should exists'); SELECT schema_privs_are ( 'demo', 'demouser', ARRAY['USAGE'], 'demouser should be granted USAGE on schema demo'); SELECT roles_are(ARRAY[ 'postgres', 'demouser', 'deploy', 'icinga']);
pg_prove -h dbhost -d dbname -U dbuser test*.sql Files=11 Tests=5742 Time=14sec Vortrag:
Teste die Datenbank ! 18/26 Thomas Koch think project! International GmbH & Co. KG
Generierung der DB-Strukturtests ●
●
komplett –
Liquibase
–
check_postgres
–
pg_tapgen
Individuell anpassen –
Struktur aus DB-Systemobjekte holen
–
Mit Filter einschränken
SELECT 'SELECT has_view(''' || table_schema || ''', ''' || table_name || ''', ''View ' || table_schema || '.' || table_name || ' should exists'');' AS pgtap FROM INFORMATION_SCHEMA.views WHERE table_schema NOT IN ('pg_catalog','information_schema') AND table_name NOT LIKE 'dba%' ORDER BY table_schema DESC Vortrag:
Teste die Datenbank ! 19/26 Thomas Koch think project! International GmbH & Co. KG
DB-Strukturtest – Übung CREATE VIEW v_mitarbeiter AS SELECT name || ', ' || vorname AS name FROM mitarbeiter;
Beispiel
CREATE OR REPLACE FUNCTION is_lastday(i_date TIMESTAMP) RETURNS BOOLEAN AS $$ SELECT CASE WHEN DATE(((date_trunc('month', i_date)+interval '1 month')interval '1 day')) = DATE(i_date) THEN TRUE ELSE FALSE END $$ LANGUAGE SQL IMMUTABLE STRICT;
Test
BEGIN; SET search_path TO pgtap, public; SELECT * FROM plan(11); test SELECT has_view('public', 'v_mitarbeiter', 'View public.v_mitarbeiter should exists'); SELECT view_owner_is( 'public', 'v_mitarbeiter', 'tkoch', 'owner of view public.v_mitarbeiter is tkoch'); SELECT lives_ok('SELECT * FROM v_mitarbeiter LIMIT 0', 'view v_mitarbeiter should run'); SELECT has_column('public', 'v_mitarbeiter', 'name', 'Column public.v_mitarbeiter.name exists'); SELECT has_function('public', 'is_lastday', 'Function public.is_lastday should exists'); SELECT is_strict('public', 'is_lastday', 'Function public.is_lastday should strict'); SELECT volatility_is( 'public', 'is_lastday', ARRAY['timestamp without time zone'], 'immutable' ); SELECT function_owner_is('public', 'is_lastday', ARRAY['timestamp without time zone'],'tkoch', 'owner of function public.is_lastday is tkoch'); SELECT function_returns('public', 'is_lastday', ARRAY['timestamp without time zone'], 'boolean', 'return type of function public.is_lastday is boolean'); SELECT function_lang_is('public', 'is_lastday', ARRAY['timestamp without time zone'], 'sql', 'language of function public.is_lastday is sql'); SELECT function_privs_are('public', 'is_lastday', ARRAY['timestamp without time zone'],'tkoch', ARRAY['EXECUTE'], 'function public.is_lastday has privileges: EXECUTE'); SELECT * FROM finish(); ROLLBACK;
Vortrag:
Teste die Datenbank ! 20/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Geschäftslogik zu testen? ●
SELECT ok(:boolean, :description);
●
SELECT is(:have, :want, :description);
●
SELECT matches(:have, :regex, :description);
●
SELECT throws_ok(:sql, :errcode, :ermsg, :description);
●
SELECT throws_like(:sql, :like, :description);
●
SELECT throws_matching(:sql, :regex, :description);
●
SELECT results_eq(:sql, :sql, :description);
●
SELECT set_eq(:sql, :sql, :description);
●
SELECT set_has(:sql, :sql, :description);
●
SELECT is_empty(:sql, :description);
●
SELECT row_eq(:sql, :record, :description);
Quelle: http://pgtap.org/documentation.html
Vortrag:
Teste die Datenbank ! 21/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Geschäftslogik zu testen? CREATE VIEW v_mitarbeiter AS SELECT name || ', ' || vorname AS name FROM mitarbeiter;
Beispiel
CREATE OR REPLACE FUNCTION is_lastday(i_date TIMESTAMP) RETURNS BOOLEAN AS $$ SELECT CASE WHEN DATE(((date_trunc('month', i_date)+interval '1 month')interval '1 day')) = DATE(i_date) THEN TRUE ELSE FALSE END $$ LANGUAGE SQL IMMUTABLE STRICT;
Test
BEGIN; SET search_path TO pgtap, public; SELECT * FROM plan(6); – prepare TRUNCATE TABLE mitarbeiter; INSERT INTO mitarbeiter (name, vorname) VALUES ('Zufall','Rainer'); – test SELECT isnt_empty($$SELECT name FROM v_mitarbeiter $$, 'test v_mitarbeiter not empty'); SELECT results_eq($$SELECT name FROM v_mitarbeiter $$, $$SELECT 'Zufall, Rainer'::TEXT $$,'test v_mitarbeiter concat'); SELECT is( is_lastday('20120229'), TRUE, 'test is_lastday from 20120229' ); SELECT is( is_lastday('20140228'), TRUE, 'test is_lastday from 20140228' ); SELECT is( is_lastday('20120228'), FALSE, 'test is_lastday from 20120228' ); SELECT is( is_lastday('20120630'), TRUE, 'test is_lastday from 20120630' );
Files=20 Tests=108 Time=4sec
SELECT * FROM finish(); ROLLBACK;
Vortrag:
Teste die Datenbank ! 22/26 Thomas Koch think project! International GmbH & Co. KG
Wie ist die Performance zu testen? SELECT performs_ok(:sql, :milliseconds, :description); SELECT performs_within(:sql, :average_milliseconds, :within, :iterations, :description);
BEGIN; SET search_path TO pgtap, public; SELECT * FROM plan(2); – prepare TRUNCATE TABLE mitarbeiter; COPY mitarbeiter FROM '/tmp/mitarbeiter_data' (DELIMITER ','); – test PREPARE fast_query AS SELECT name FROM v_mitarbeiter WHERE name LIKE 'Zufall'; SELECT performs_ok('fast_query', 250, 'select by name should be fast'); SELECT performs_within('fast_query', 250, 10, 100, 'select by name should be fast'); SELECT * FROM finish(); ROLLBACK;
Quelle: http://pgtap.org/documentation.html
Vortrag:
Teste die Datenbank ! 23/26 Thomas Koch think project! International GmbH & Co. KG
Automatisierung ●
pg_prove in Build-Tools integrieren –
Jenkins (TAP Plugin bzw. xUnit Plugin)
–
Buildbot
●
pg_prove in Deployment-Skripte integrieren
●
pg_prove in crontab aufnehmen
Vortrag:
Teste die Datenbank ! 24/26 Thomas Koch think project! International GmbH & Co. KG
Zusammenfassung ●
●
●
●
Warum? –
Fundament eines Systems
–
nur eine weitere Komponente
–
Geschäftmodell und Geschäftslogik
Wann und Wo? –
Eher direkt auf der Datenbank
–
mehr Unit Tests als Integrationstests
Was? –
Struktur der Datenbank
–
Geschäftslogik in der Datenbank
–
Performance der Datenbank
Womit? –
●
für jede DB gibt es mehrere Alternativen (hier pgTAP gezeigt)
Wie? –
am Besten in einer Transaktion mit Rollback am Ende
–
im Vorbereitungsschritt die Daten löschen und mit eigenen Daten befüllen
–
Tests der DB-Struktur können generiert werden
Vortrag:
Teste die Datenbank ! 25/26 Thomas Koch think project! International GmbH & Co. KG
Teste die Datenbank !
Thomas Koch Senior Database Specialist think project! International GmbH & Co. KG Mail:
[email protected]