Teste die Datenbank!

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('2012­02­29'), TRUE, 'test is_lastday from 2012­02­29' );
SELECT is( is_lastday('2014­02­28'), TRUE, 'test is_lastday from 2014­02­28' );
SELECT is( is_lastday('2012­02­28'), FALSE, 'test is_lastday from 2012­02­28' );
SELECT is( is_lastday('2012­06­30'), TRUE, 'test is_lastday from 2012­06­30' );
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]