Power Query: testy jakości danych jako „unit testy” — 12 asercji, które łapią błędy przed publikacją

Jak wdrożyć testy jakości danych w Power Query jak unit testy: 12 asercji, gotowe funkcje M, raport błędów i szablon przepływu, który wykrywa problemy przed publikacją.
29 czerwca 2026
blog

1. Dlaczego testy jakości danych w Power Query: analogia do unit testów i miejsce w pipeline ETL/ELT

W praktyce raportowania i analityki większość „błędów w danych” nie wygląda jak awaria systemu. Zazwyczaj odświeżanie przechodzi, modele się ładują, a problem wychodzi dopiero na wykresie: znikające wiersze, podwójne rekordy, dziwne wartości w kluczach, nagłe skoki miar. Testy jakości danych w Power Query pozwalają przenieść wykrywanie takich problemów wcześniej, czyli na etap przygotowania danych, zanim trafią do modelu lub raportu.

Można o nich myśleć jak o unit testach dla danych: krótkich, powtarzalnych sprawdzeniach, które potwierdzają, że wynik transformacji spełnia założone reguły. Nie testują one „czy kod się uruchamia”, tylko „czy dane po uruchomieniu są nadal sensowne”. Taka perspektywa porządkuje pracę: transformacje stają się odpowiednikiem funkcji, a testy weryfikują ich kontrakt wejście/wyjście.

Unit testy w kodzie vs. „unit testy” danych

Analogia jest bardzo użyteczna, ale warto jasno rozdzielić, co jest testowane:

  • Unit testy w programowaniu zwykle weryfikują zachowanie funkcji dla kontrolowanych danych wejściowych i sprawdzają konkretne wartości wyjściowe.
  • Testy jakości danych weryfikują własności zbioru danych: spójność, kompletność, unikalność, zakresy wartości, zgodność typów, relacje między kolumnami czy stabilność schematu.

Różnica jest kluczowa: w danych rzadko oczekujesz identycznych wartości rekord-po-rekordzie między odświeżeniami. Zamiast tego oczekujesz, że dane będą spełniały reguły biznesowe i techniczne. Test jakości to więc bardziej asercja o stanie danych niż porównanie do „złotego wyniku”.

Po co robić to w Power Query

Power Query jest naturalnym miejscem na takie sprawdzenia, bo to właśnie tu:

  • następuje łączenie źródeł, filtrowanie, mapowanie i czyszczenie danych,
  • powstaje finalny zestaw danych ładowany do modelu,
  • najłatwiej przechwycić błędy wynikające ze zmian w źródle (np. inny typ kolumny, brak kolumny, nowe wartości spoza zakresu).

Dodatkowo testy w Power Query mogą działać niezależnie od tego, czy dane trafiają do modelu w Power BI, do arkusza w Excelu, czy są tylko etapem pośrednim w przygotowaniu danych. Najważniejsze jest, że są uruchamiane automatycznie przy odświeżeniu, czyli dokładnie wtedy, kiedy ryzyko „cichej degradacji jakości” jest największe.

Miejsce w pipeline ETL/ELT

W pipeline ETL/ELT testy jakości danych pełnią rolę bramki kontrolnej między transformacją a publikacją/udostępnieniem danych. W zależności od architektury mogą wystąpić w kilku punktach:

  • Po ekstrakcji — by szybko wykryć, że źródło dostarczyło dane niekompletne, w innej strukturze lub o podejrzanej skali.
  • Po kluczowych transformacjach — by sprawdzić, czy łączenia, deduplikacje i mapowania nie wprowadziły utraty rekordów albo niepożądanych nulli.
  • Tuż przed załadowaniem — jako ostatnia walidacja kontraktu danych dostarczanych do modelu/raportu.

W podejściu ETL testy często „pilnują” efektu końcowego transformacji. W ELT część reguł może być wykonywana później (np. w warstwie silnika SQL), ale Power Query nadal bywa miejscem, w którym najszybciej i najbliżej użytkownika biznesowego da się zdefiniować i utrzymać podstawowe oczekiwania jakościowe.

Jakie problemy łapią najszybciej

Największa wartość testów jakości w Power Query pojawia się tam, gdzie błędy są częste, a skutki kosztowne:

  • Zmiany schematu: zniknięcie/zmiana nazwy kolumny, zmiana typu, dodatkowe kolumny wpływające na logikę.
  • Regresje w danych: nagły spadek liczby wierszy, duża liczba nulli w polach krytycznych, nietypowe wartości w kluczach.
  • Błędy łączeń: duplikacja po joinie, nieoczekiwane rekordy „niezmatchowane”, rozjazdy kluczy.
  • Niezgodność ze słownikami: wartości spoza dozwolonego zestawu, mieszanie formatów, nowe kategorie bez obsługi.

Istotne jest to, że takie problemy często nie powodują błędu technicznego. Testy jakości mają je wyświetlić i nazwać, zanim ktoś podejmie decyzję na podstawie błędnych danych.

Co daje taka praktyka zespołowi

Wprowadzenie testów jakości jako „unit testów” zmienia pracę z Power Query z reaktywnej na przewidywalną:

  • Większa pewność odświeżeń — mniej sytuacji, w których raport „działa”, ale pokazuje błędne wnioski.
  • Szybsza diagnostyka — gdy coś się psuje, wiadomo co i gdzie nie spełnia reguł.
  • Lepsza komunikacja — reguły jakości stają się jawne i powtarzalne, zamiast ukrytych w założeniach.
  • Stabilniejsza publikacja — mniejsza liczba „cichych” zmian w danych, które psują miary, relacje i interpretację.

To nie jest dodatkowa „papierologia” wokół danych, tylko praktyczny mechanizm kontroli jakości osadzony w miejscu, w którym i tak wykonujesz transformacje. Dzięki temu Power Query staje się nie tylko narzędziem do przygotowania danych, ale też pierwszą linią obrony przed błędami, które zwykle ujawniają się zbyt późno.

2. Architektura podejścia: warstwy zapytań, funkcje testujące, konwencje nazewnicze i dane wejściowe/wyjściowe

Żeby testy jakości danych działały jak „unit testy”, muszą być odseparowane od logiki biznesowej, powtarzalne i uruchamialne w sposób kontrolowany. W Power Query najprościej osiągnąć to przez świadome ułożenie zapytań w warstwy, wprowadzenie zestawu funkcji testujących oraz jasnych konwencji nazewniczych i kontraktów wejścia/wyjścia. Podczas szkoleń Cognity ten temat wraca regularnie – dlatego zdecydowaliśmy się go omówić również tutaj. Taka architektura zmniejsza ryzyko, że testy staną się przypadkowym fragmentem transformacji lub że będą trudne do utrzymania przy zmianach schematu.

Warstwy zapytań: gdzie kończy się transformacja, a zaczyna walidacja

W praktyce warto rozdzielić pipeline na logiczne warstwy, które można myśleć jak o odpowiednikach modułów w kodzie:

  • Warstwa źródłowa (staging) — zapytania pobierające dane z systemów źródłowych z minimalną ingerencją. Celem jest stabilne i przewidywalne „odtworzenie” danych wejściowych oraz ograniczenie transformacji, które utrudniają diagnozę błędów.
  • Warstwa przygotowania (standardization/cleanup) — ujednolicanie typów, formatów, nazw kolumn i podstawowe czyszczenie. Tu eliminuje się różnice techniczne, zanim zacznie się oceniać reguły jakości.
  • Warstwa modelująca (business logic) — transformacje biznesowe: łączenia, wyliczenia, mapowania, agregacje. To zwykle „docelowe” tabele do modelu lub raportu.
  • Warstwa testów (validation) — osobne zapytania uruchamiające asercje na wybranych obiektach z wcześniejszych warstw. Kluczowe jest, by testy nie modyfikowały danych, tylko je oceniały i zwracały wynik walidacji.
  • Warstwa publikacji (outputs) — końcowe zapytania, które trafiają do modelu danych/raportu albo do dalszych etapów. Publikacja powinna być możliwa dopiero, gdy testy dają akceptowalne wyniki (w zależności od przyjętej strategii).

Najważniejsza różnica między warstwą modelującą a testową polega na odpowiedzialności: modelowanie tworzy dane, a testy oceniają dane. Dzięki temu reguły jakości można rozwijać niezależnie od transformacji i łatwiej utrzymać przejrzystość.

Funkcje testujące: standardowe „klocki” zamiast testów pisanych od zera

Jednolity zestaw funkcji testujących działa jak biblioteka narzędziowa. Zamiast za każdym razem budować ręcznie logikę walidacji, korzysta się z funkcji o stałym interfejsie. Architektonicznie warto dążyć do tego, aby:

  • każda funkcja testująca odpowiadała jednej klasie reguł (np. kompletność, unikalność, zakres wartości, spójność między tabelami),
  • funkcje zwracały ujednolicony wynik (tak, aby można je było łączyć w jeden raport),
  • funkcje przyjmowały jawne parametry (np. nazwa kolumny, oczekiwany typ, dopuszczalne wartości), co zmniejsza zależność od kontekstu zapytania.

Ważne jest też rozróżnienie dwóch poziomów: pojedynczy test (jedna asercja na jednym obiekcie) oraz zestaw testów (grupowanie wielu asercji uruchamianych na tabeli lub obszarze danych). Dzięki temu można budować walidację modułowo: testy da się dodawać i usuwać bez przebudowy całego przepływu.

Konwencje nazewnicze: czytelność, nawigacja i przewidywalne zależności

Power Query szybko rozrasta się do wielu zapytań, a testy dodają kolejną warstwę. Bez konwencji nazewniczych rośnie ryzyko pomyłek: testy uruchamiane na złej tabeli, przypadkowe ładowanie zapytań pomocniczych lub trudność w ustaleniu, co jest „produktem”, a co „narzędziem”. Spójna konwencja powinna:

  • wyraźnie odróżniać zapytania danych od zapytań testowych i pomocniczych,
  • umożliwiać szybkie znalezienie: „jakie testy dotyczą tej tabeli?”,
  • ograniczać niejawne zależności (np. testy oparte o „ostatni krok” w edytorze zamiast o wskazany obiekt).

Praktycznie dobrze sprawdzają się prefiksy wskazujące rolę zapytania (np. staging/transform/model/test) oraz konsekwentne nazywanie testów w formie: co testuję + jaka reguła. Dzięki temu lista zapytań staje się mapą pipeline, a nie przypadkowym zbiorem.

Dane wejściowe i wyjściowe: kontrakt testów jakości

Testy jakości danych powinny mieć prosty i stabilny kontrakt.

  • Wejście — zwykle jedna tabela (lub para tabel w testach relacyjnych) oraz parametry opisujące regułę. Kluczowe jest, by testy działały na określonym etapie pipeline (np. po ujednoliceniu typów), bo wtedy wynik jest interpretowalny i porównywalny w czasie.
  • Wyjście — nie „poprawione dane”, lecz wynik walidacji. Najczęściej jest to zbiór informacji umożliwiających automatyczne decyzje (czy przepuścić publikację) oraz diagnostykę (co i gdzie jest nie tak). Dobrze, gdy wynik da się łączyć z wynikami innych testów w jeden raport oraz filtrować po obiekcie, regule i istotności.

Istotne rozróżnienie dotyczy tego, czy wyjściem ma być wyłącznie sygnał „OK/FAIL”, czy również materiał do analizy. Architektura, która przewiduje oba scenariusze, pozwala z jednej strony blokować publikację przy krytycznych naruszeniach, a z drugiej — utrzymać pełną widoczność jakości danych bez ręcznego przekopywania transformacji.

Organizacja zapytań: co powinno być ładowane, a co tylko wspierać

W podejściu „testowym” część zapytań pełni rolę infrastruktury (funkcje, parametry, pomocnicze kroki) i nie powinna trafiać do modelu czy raportu. Architektonicznie warto od początku rozdzielić:

  • artefakty produkcyjne — tabele, które mają być konsumowane dalej,
  • artefakty testowe — wyniki walidacji i ewentualne zapytania agregujące testy,
  • artefakty techniczne — funkcje i elementy wspólne, które mają ułatwiać utrzymanie, ale nie są ani danymi biznesowymi, ani raportem jakości.

Taki podział zmniejsza ryzyko przypadkowego „zaśmiecania” modelu obiektami pomocniczymi i ułatwia kontrolę nad tym, co jest publikowane oraz odświeżane.

3. Zestaw 12 asercji jakości danych: definicje, kiedy stosować i jakie zwracają wyniki

Poniższe asercje traktuj jak „unit testy” dla danych w Power Query: każda ma jasny warunek, określony zakres (tabela/kolumna) oraz jednoznaczny wynik do dalszego raportowania. Dobrą praktyką jest, aby asercja zwracała co najmniej: Pass/Fail, liczbę naruszeń oraz (opcjonalnie) próbkę rekordów naruszających warunek.

Asercja Definicja (co sprawdza) Kiedy stosować Typowy wynik
1) Schema / kolumny wymagane Sprawdza, czy tabela zawiera wymagane kolumny (i ewentualnie, czy nie brakuje żadnej z listy). Gdy źródło bywa zmienne (nowe/zmienione kolumny), integracje z API, pliki CSV/XLS. Pass/Fail; lista brakujących (i opcjonalnie nadmiarowych) kolumn.
2) Typy danych (type conformance) Weryfikuje zgodność typów (np. liczba, data, tekst) z kontraktem; wykrywa wartości, których nie da się poprawnie rzutować. Po łączeniu źródeł, przy importach tekstowych, gdy pojawiają się „prawie daty” albo liczby z separatorami. Pass/Fail; liczba wartości niezgodnych; wskazanie kolumn.
3) Brak wartości null w kolumnach krytycznych Sprawdza, czy wskazane kolumny nie zawierają null (lub pustych wartości, jeśli tak zdefiniujesz). Dla kluczy, dat zdarzeń, kwot, statusów — wszędzie tam, gdzie null psuje logikę miar lub joinów. Pass/Fail; liczba nulli; próbka wierszy z null.
4) Unikalność klucza (no duplicates) Weryfikuje, że klucz biznesowy/techniczny jest unikalny (brak duplikatów dla wskazanych kolumn). W tabelach faktów i wymiarów, przed relacjami 1:*; przy inkrementalnych dopięciach danych. Pass/Fail; liczba duplikujących się kluczy; lista kluczy z licznością.
5) Integralność referencyjna (foreign key exists) Sprawdza, czy wartości klucza obcego istnieją w tabeli referencyjnej (brak „sierot”). Po joinach, w modelowaniu gwiazdy, gdy wymiary są ładowane osobno od faktów. Pass/Fail; liczba sierot; próbka brakujących kluczy.
6) Dozwolone wartości (domain / list membership) Weryfikuje, że kolumna przyjmuje wartości tylko z dozwolonego zbioru (np. statusy, kody, kategorie). Gdy upstream może dopisać nowy status bez uzgodnienia; przy mapowaniach i słownikach. Pass/Fail; lista wartości spoza domeny; liczność.
7) Zakresy i progi (range check) Sprawdza, czy wartości liczbowe/daty mieszczą się w określonym zakresie (min/max) lub spełniają progi. Dla kwot, ilości, wieku, kursów; wykrywanie wartości ujemnych, astronomicznych lub „dat z przyszłości”. Pass/Fail; liczba outlierów; min/max znalezione; próbka naruszeń.
8) Reguły formatu (pattern / length) Weryfikuje format tekstu: długość, prefix/suffix, prosty wzorzec (np. e-mail, kod pocztowy, NIP w formie). Gdy identyfikatory są tekstowe, a błędy OCR/CSV wprowadzają spacje, myślniki, złe długości. Pass/Fail; liczba wartości niespełniających; przykłady błędnych wartości.
9) Spójność między kolumnami (cross-field consistency) Sprawdza zależności typu „jeśli A, to B”: np. data_zakończenia ≥ data_startu; status=Zamknięty ⇒ data_zamknięcia nie-null. W procesach workflow, zamówieniach, reklamacjach, subskrypcjach — wszędzie, gdzie logika jest warunkowa. Pass/Fail; liczba naruszeń; próbka wierszy z konfliktem.
10) Kontrola świeżości (freshness / max date) Sprawdza, czy dane są aktualne: np. maksymalna data w tabeli ≥ (dzisiaj − N dni) lub czy jest rekord dla ostatniego okresu. Gdy raporty mają SLA; przy zasilaniu dziennym/tygodniowym; przy ryzyku „odświeżenie się nie wydarzyło”. Pass/Fail; wykryta maksymalna data; różnica dni; brakujące okresy.
11) Test wolumenu (row count / expected volume) Sprawdza liczbę wierszy względem oczekiwań: minimum, maksimum lub odchylenie od poprzedniego okresu. Do wykrywania uciętych plików, pustych zrzutów, podwójnych ładowań, filtrów po drodze. Pass/Fail; liczba wierszy; odchylenie %; informacja o progu.
12) „Brak nieoczekiwanych nulli po joinie” (join completeness) Po połączeniu tabel sprawdza, czy kolumny z tabeli dołączanej nie są null (co zwykle oznacza brak dopasowania). Gdy join ma być pełny (np. każdy fakt musi mieć wymiar), a null po joinie oznacza błąd mapowania/klucza. Pass/Fail; liczba wierszy bez dopasowania; próbka kluczy powodujących null.

Uwaga praktyczna: te same asercje mogą działać na różnych poziomach — kolumny (typ, null, format), wiersza (spójność między polami), tabeli (wolumen, świeżość) oraz relacji (integralność, kompletność joinu). Dzięki temu łatwo zbudować zestaw testów dopasowany do ryzyka w konkretnym przepływie danych.

// Minimalny „kontrakt” wyniku asercji (przykład struktury, nie implementacja)
// [TestName=text, Passed=logical, Violations=number, Details=table]

4. Implementacja asercji w języku M: przykładowe funkcje i fragmenty kodu dla typowych testów

W Power Query „asercje” najwygodniej implementować jako funkcje M, które przyjmują tabelę (oraz parametry testu) i zwracają wynik testu w ustandaryzowanej postaci. W praktyce spotkasz dwa podejścia:

  • Fail-fast: funkcja zwraca tabelę, a gdy test nie przechodzi — rzuca błąd przez error (blokuje publikację/odświeżenie).
  • Raportowanie: funkcja zawsze zwraca rekord/tabelę z metadanymi testu (pass/fail, liczność naruszeń, próbka), a decyzja o przerwaniu procesu zapada później.

W tej sekcji skupiamy się na tym, jak technicznie buduje się takie funkcje i jakich klocków języka M używać, bez rozstrzygania jeszcze konwencji i pełnej architektury uruchamiania testów. W czasie szkoleń Cognity ten temat bardzo często budzi ożywione dyskusje między uczestnikami — głównie dlatego, że wybór między „raportować” a „blokować odświeżenie” ma realne konsekwencje dla procesów i odpowiedzialności w zespole.

4.1. Minimalny „kontrakt” funkcji testującej

Najmniej kłopotliwe w utrzymaniu jest, gdy każda asercja zwraca rekord (lub jednorzędową tabelę) o stałych polach. Dzięki temu łatwo je składać w zbiorczy raport.

/* Zwracany rekord: przykład minimalnego kontraktu */
[
  Test = "NotNull: OrderDate",
  Passed = true,
  Violations = 0,
  Severity = "Error",
  Message = "OK",
  Sample = #table({}, {})
]

Kluczowe elementy, które zwykle chcesz mieć w wyniku:

  • Test — czytelna nazwa testu (np. typ + kolumna).
  • Passed — boolean.
  • Violations — liczba naruszeń (np. liczba nulli, duplikatów).
  • Severity — np. Error/Warning (przydatne przy progach akceptacji).
  • Sample — opcjonalna próbka wierszy łamiących regułę (do diagnostyki).

4.2. Klocki języka M używane w testach

Poniżej skrót najczęściej używanych mechanizmów, które pozwalają pisać asercje zwięźle i wydajnie:

  • Filtrowanie naruszeń: Table.SelectRows (najprostsze), czasem Table.FindText / Text.Contains dla tekstu.
  • Liczenie naruszeń: Table.RowCount.
  • Próbka: Table.FirstN (np. 20 pierwszych błędnych wierszy).
  • Sprawdzanie kolumn: Table.HasColumns, Table.ColumnNames.
  • Unikalność: Table.Distinct, Table.Group.
  • Typy: Value.Is, Type.Is, Value.Type (uwaga na null).
  • Warunkowe przerwanie: if ... then ... else error ....
  • Bezpieczne wykonywanie: try ... otherwise ... (gdy test ma przechwycić wyjątek i zamienić go w wynik „fail”).

4.3. Wzorzec: asercja „wyfiltruj naruszenia → policz → zbuduj wynik”

Większość testów jakości danych można sprowadzić do prostego wzorca: zidentyfikuj wiersze naruszające regułę, policz je i zwróć metryki wraz z próbką.

(
  tbl as table,
  testName as text,
  severity as text,
  badRows as table,
  optional sampleSize as number
) as record =>
let
  N = if sampleSize = null then 20 else sampleSize,
  v = Table.RowCount(badRows),
  passed = v = 0,
  sample = Table.FirstN(badRows, N),
  msg = if passed then "OK" else "Violations: " & Text.From(v)
in
  [Test = testName, Passed = passed, Violations = v, Severity = severity, Message = msg, Sample = sample]

Ten fragment nie jest jeszcze „konkretną” asercją, ale pokazuje ideę: oddzielasz logikę wykrywania naruszeń od logiki raportowania wyniku. Ułatwia to spójność i ogranicza duplikację kodu.

4.4. Przykładowe asercje: najczęstsze testy w M

Poniższe funkcje to krótkie, typowe implementacje. Każda asercja tworzy tabelę naruszeń, a potem składa wynik do rekordu.

4.4.1. Asercja: brak wartości null w kolumnie

(tbl as table, col as text, optional severity as text) as record =>
let
  sev = if severity = null then "Error" else severity,
  bad = Table.SelectRows(tbl, each Record.Field(_, col) = null),
  v = Table.RowCount(bad)
in
  [
    Test = "NotNull: " & col,
    Passed = v = 0,
    Violations = v,
    Severity = sev,
    Message = if v = 0 then "OK" else "Nulls in column: " & col,
    Sample = Table.FirstN(bad, 20)
  ]

Zastosowanie: pola wymagane (klucze, daty dokumentu, identyfikatory). To jeden z najbardziej „unit-testowych” testów: prosty, jednoznaczny i częsty.

4.4.2. Asercja: unikalność klucza (bez duplikatów)

(tbl as table, keyCols as list, optional severity as text) as record =>
let
  sev = if severity = null then "Error" else severity,
  grp = Table.Group(
    tbl,
    keyCols,
    { {"__cnt", each Table.RowCount(_), Int64.Type} }
  ),
  badKeys = Table.SelectRows(grp, each [__cnt] > 1),
  // opcjonalnie: dołącz przykładowe wiersze źródłowe dla kluczy zduplikowanych
  bad = if Table.RowCount(badKeys) = 0 then #table(Table.ColumnNames(tbl), {})
        else Table.Join(tbl, keyCols, badKeys, keyCols, JoinKind.Inner),
  v = Table.RowCount(badKeys)
in
  [
    Test = "UniqueKey: " & Text.Combine(keyCols, ","),
    Passed = v = 0,
    Violations = v,
    Severity = sev,
    Message = if v = 0 then "OK" else "Duplicate keys: " & Text.From(v),
    Sample = Table.FirstN(bad, 20)
  ]

Zastosowanie: tabele faktów i wymiarów, rekordy transakcyjne, integracje, gdzie duplikat oznacza podwójne księgowanie lub błędne agregacje.

4.4.3. Asercja: zakres wartości (np. liczby nieujemne)

(tbl as table, col as text, min as nullable number, max as nullable number, optional severity as text) as record =>
let
  sev = if severity = null then "Error" else severity,
  bad = Table.SelectRows(tbl, each
    let v = Record.Field(_, col) in
      v <> null and (
        (min <> null and v < min) or
        (max <> null and v > max)
      )
  ),
  cnt = Table.RowCount(bad)
in
  [Test = "Range: " & col, Passed = cnt = 0, Violations = cnt, Severity = sev,
   Message = if cnt = 0 then "OK" else "Out of range: " & col,
   Sample = Table.FirstN(bad, 20)]

Zastosowanie: ilości, kwoty, stawki, procenty, indeksy. Dobre do wyłapania przesuniętych przecinków, błędów waluty lub importu tekstu jako liczby.

4.4.4. Asercja: dozwolone wartości (słownik / lista)

(tbl as table, col as text, allowed as list, optional severity as text) as record =>
let
  sev = if severity = null then "Error" else severity,
  bad = Table.SelectRows(tbl, each
    let v = Record.Field(_, col) in
      v <> null and not List.Contains(allowed, v)
  ),
  cnt = Table.RowCount(bad)
in
  [Test = "AllowedValues: " & col, Passed = cnt = 0, Violations = cnt, Severity = sev,
   Message = if cnt = 0 then "OK" else "Unexpected value(s) in: " & col,
   Sample = Table.FirstN(bad, 20)]

Zastosowanie: statusy, typy dokumentów, flagi, wartości z enumeracji. Przydatne także do wychwytywania literówek i różnic w kodowaniu.

4.4.5. Asercja: typ danych „da się zrzutować” (castable)

W Power Query często problemem nie jest to, że typ jest „zły”, tylko że część wierszy nie daje się poprawnie skonwertować. Wtedy test warto oprzeć o try, a nie o deklaracje typów.

(tbl as table, col as text, target as text, optional severity as text) as record =>
let
  sev = if severity = null then "Error" else severity,
  bad = Table.SelectRows(tbl, each
    let v = Record.Field(_, col) in
      v <> null and
      (
        if target = "number" then (try Number.From(v) otherwise null) = null
        else if target = "date" then (try Date.From(v) otherwise null) = null
        else if target = "datetime" then (try DateTime.From(v) otherwise null) = null
        else false
      )
  ),
  cnt = Table.RowCount(bad)
in
  [Test = "Castable: " & col & "->" & target, Passed = cnt = 0, Violations = cnt, Severity = sev,
   Message = if cnt = 0 then "OK" else "Uncastable values in: " & col,
   Sample = Table.FirstN(bad, 20)]

Zastosowanie: importy z CSV/Excel, integracje API, pola dat i liczb trzymane jako tekst. Ten test często łapie „ciche” błędy na granicy lokalizacji (kropka/przecinek, format daty).

4.5. Dwa warianty wykonania: raport vs. przerwanie odświeżania

To, czy asercja ma przerywać odświeżanie, to decyzja „egzekucyjna”. Technicznie w M różnica sprowadza się do tego, czy po zbudowaniu wyniku rzucasz błąd.

Wariant Zachowanie Mechanizm w M
Raportujący Zwraca wynik testu zawsze zwykły rekord/tabela
Fail-fast Przerywa, jeśli Passed=false if Passed then ... else error ...
let
  r = MyAssertion(tbl),
  out = if r[Passed] then r else error r[Message]
in
  out

W praktyce wygodne jest utrzymywanie testów jako raportujących, a fail-fast realizować na końcu (lub dla wybranych severity). Ale mechanicznie oba podejścia są proste.

4.6. Uwagi implementacyjne: wydajność i „sklejanie” testów

  • Minimalizuj skanowanie danych: każdy test oparty o Table.SelectRows to osobne przejście po tabeli. Jeśli tabela jest duża, rozważ łączenie reguł lub testowanie na zredukowanym widoku (np. tylko kluczowe kolumny).
  • Próbkuj świadomie: próbka (FirstN) jest tania, ale budowanie pełnej tabeli naruszeń bywa kosztowne. Czasem wystarczy policzyć i pobrać pierwsze N błędów.
  • Unikaj kruchych odwołań: korzystaj z Record.Field(_, col) zamiast _[Kolumna], gdy kolumna jest parametrem tekstowym.
  • Przechwytuj błędy konwersji: testy typu/castowania rób przez try, inaczej pojedynczy zły wiersz może przerwać cały query zamiast zostać policzony jako naruszenie.
💡 Pro tip: Ustandaryzuj „kontrakt” wyniku każdej asercji (Test/Passed/Violations/Severity/Message/Sample), a logikę wykrywania naruszeń oddziel od składania rekordu — dzięki temu testy łatwo łączyć i utrzymywać. Gdy kolumna jest parametrem, używaj Record.Field i try/otherwise do konwersji, aby pojedyncza zła wartość nie wywracała całego odświeżenia, tylko trafiała do Violations.

5. Raportowanie i egzekwowanie wyników: tabela błędów, fail-fast vs. pełny raport, progi akceptacji i severity

Same asercje to dopiero połowa sukcesu — druga połowa to czytelne raportowanie i konsekwentne egzekwowanie wyników. W Power Query kluczowe jest, by testy zwracały wyniki w formie, którą da się: (1) łatwo przejrzeć w edytorze, (2) wykorzystać do przerwania odświeżania, (3) archiwizować i monitorować w czasie.

Tabela błędów jako kontrakt wyjścia testów

Najpraktyczniejszym formatem raportu jest pojedyncza tabela, w której każdy wiersz opisuje jedną niezgodność (albo agregat niezgodności) wykrytą przez asercję. Taki „contract” ułatwia łączenie wyników wielu testów, filtrowanie oraz budowę warunków „pass/fail”.

Minimalny zestaw kolumn, który zwykle wystarcza:

  • TestName – jednoznaczna nazwa asercji (stabilna, pod którą będzie raportowana).
  • Entity – obiekt testowany (np. nazwa zapytania / tabela / domena danych).
  • Column – kolumna, jeśli dotyczy (może być pusta dla testów tabelowych).
  • Severity – poziom ważności (np. Critical/High/Medium/Low lub 1–4).
  • ErrorCount – liczba wykrytych naruszeń (0 oznacza „pass” i zwykle nie trafia do tabeli).
  • Sample – krótka próbka (np. pierwsza wartość naruszająca), bez wylewania całej diagnostyki.
  • Message – opis zwięzły, zorientowany na działanie (co jest nie tak i gdzie).
  • RunId / RefreshTime – identyfikator uruchomienia lub znacznik czasu (przydatne w historii).

Ważna zasada: tabela błędów powinna być „appendowalna” — tzn. każda asercja zwraca wynik w tym samym schemacie, dzięki czemu można je bez tarcia łączyć (Append) w jeden raport.

Fail-fast vs. pełny raport: dwa tryby pracy

W praktyce spotyka się dwa tryby egzekwowania testów, a wybór zależy od tego, czy ważniejsza jest szybkość informacji zwrotnej czy pełna diagnoza.

Tryb Co robi Kiedy używać Ryzyko / koszt
Fail-fast Przerywa odświeżanie przy pierwszym błędzie (lub pierwszym krytycznym) Produkcja, krytyczne dashboardy, gdy błąd oznacza „nie publikować” Mniej informacji diagnostycznej w jednym przebiegu
Pełny raport Uruchamia wszystkie testy i zwraca komplet naruszeń Utrzymanie, wdrożenia, refaktoring, analiza regresji Dłuższy czas odświeżania i większa ilość danych diagnostycznych

Praktyczny kompromis: generować pełny raport, ale egzekwowanie (blokada publikacji) opierać tylko o wybrane severity lub przekroczenie progów. Dzięki temu dostajesz szeroką diagnozę, a jednocześnie zachowujesz kontrolę „go/no-go”.

Progi akceptacji: kiedy błąd jest „dopuszczalny”

Nie każda niezgodność musi oznaczać zatrzymanie pipeline’u. W danych operacyjnych zdarzają się odstępstwa, które są znane i akceptowane (np. pojedyncze braki w polu opcjonalnym). Dlatego testy jakości danych warto oceniać nie tylko binarnie, ale też przez progi akceptacji.

  • Próg bezwzględny (np. maks. 10 wierszy naruszeń) – dobry, gdy wolumen jest względnie stały.
  • Próg procentowy (np. maks. 0,5% naruszeń) – lepszy, gdy wolumen silnie się zmienia.
  • Warunkowy próg per segment (np. osobno dla kraju/źródła/systemu) – gdy różne części danych mają różną „trudność” jakościową.

W raporcie warto więc trzymać zarówno ErrorCount, jak i (jeśli dostępne) TotalCount oraz wyliczone ErrorRate. Egzekwowanie decyzji „pass/fail” może wtedy bazować na: (a) przekroczeniu progu, (b) severity, (c) kombinacji obu.

Severity: priorytetyzacja i konsekwencje

Severity porządkuje testy według wpływu na biznes i ryzyka błędnej publikacji. Typowy podział:

  • Critical – błąd blokuje publikację (np. klucze duplikują się, brak wymaganych pól, niepoprawne typy uniemożliwiają miary).
  • High – zwykle blokuje lub wymaga decyzji; może powodować błędne wnioski (np. niezgodność zakresów dat, niespójność referencyjna).
  • Medium – nie blokuje, ale wymaga naprawy; wpływ ograniczony (np. braki w polu opisowym, pojedyncze odstępstwa od słownika).
  • Low – informacyjne/monitorujące; służy trendom i higienie danych.

Dzięki severity możesz wprowadzić jasne reguły:

  • Blokuj odświeżanie, gdy istnieje dowolny błąd Critical (albo Critical/High).
  • Ostrzegaj i raportuj, gdy występują Medium/Low, ale pozwalaj publikować.
  • Akceptuj pewne znane wyjątki przez progi (np. Medium do 0,2%).

Egzekwowanie w praktyce: „bramka” jakości

Egzekwowanie to mechanizm, który zamienia raport w decyzję. Najczęściej przyjmuje postać osobnego kroku/zapytania, które:

  • zbiera wyniki testów do jednej tabeli,
  • stosuje reguły (severity + progi),
  • albo zwraca pusty wynik / status OK, albo rzuca błąd blokujący odświeżanie.

Poniżej minimalny przykład idei „bramki” (schematycznie):

// WynikiTestow: tabela z kolumnami m.in. Severity, ErrorCount
// Reguła: blokuj, jeśli istnieje jakikolwiek Critical z ErrorCount > 0
let
  CriticalFailures = Table.SelectRows(WynikiTestow, each [Severity] = "Critical" and [ErrorCount] > 0),
  Gate = if Table.RowCount(CriticalFailures) > 0
         then error "Data quality gate failed: Critical errors detected."
         else "OK"
in
  Gate

Ważne: nawet jeśli stosujesz fail-fast, opłaca się równolegle utrzymywać tabelę błędów jako artefakt diagnostyczny (do wglądu podczas naprawy). W trybie pełnego raportu ta sama tabela jest natywnym wynikiem.

Jak projektować komunikaty, żeby były użyteczne

Raport testów ma skracać czas naprawy. Dlatego komunikaty powinny być:

  • konkretne (co jest nie tak, w jakiej kolumnie/encjach),
  • mierzalne (ile rekordów dotyczy),
  • akcjonowalne (co sprawdzić jako pierwsze: źródło, mapowanie, typy, join).

Unikaj w raporcie „ściany wartości” — zamiast tego dawaj liczby + próbkę. Szczegóły (pełne listy rekordów) utrzymuj jako opcjonalny, dodatkowy artefakt, jeśli jest potrzebny do analizy.

6. Gotowy szablon przepływu testowego w Power Query: struktura zapytań, parametry, uruchamianie i integracja z odświeżaniem

Poniżej znajduje się praktyczny, powtarzalny szablon „przepływu testowego” w Power Query, który można skopiować między projektami. Celem jest stałe miejsce na testy, jednoznaczny wynik (PASS/FAIL) oraz łatwa integracja z odświeżaniem — bez rozbudowywania logiki biznesowej w samych testach.

6.1. Struktura zapytań: minimalny układ warstw

Najprostszy, czytelny układ to rozdzielenie zapytań na cztery role:

  • Źródła (SRC) — połączenia do plików/baz/API, bez „sprzątania”.
  • Transformacje (STG/INT) — czyszczenie, typowanie, łączenia, logika przygotowująca dane.
  • Publikacje (PUB) — finalne tabele ładowane do modelu/arkusza.
  • Testy (TST) — zapytania i/lub funkcje, które oceniają jakość danych na wejściu/wyjściu.

W praktyce testy warto podczepiać w dwóch punktach:

  • Na wejściu (po SRC lub wczesnym STG) — łapanie problemów w danych źródłowych i kontraktach.
  • Na wyjściu (przed PUB) — ochrona przed błędami transformacji i regresją.
Element Co zwraca Ładowanie Po co
SRC_* Tabela „surowa” Najczęściej wyłączone Stabilny punkt wejścia, łatwe debugowanie źródła
STG_/INT_* Tabela po transformacjach Zwykle wyłączone Warstwa robocza, do której podpina się testy
PUB_* Tabela finalna włączone Dane konsumowane przez raport/model
TST_* Raport testów (tabela) Zależnie od narzędzia: często włączone jako diagnostyka Jedno miejsce, gdzie widać PASS/FAIL i listę naruszeń

6.2. Konwencje nazewnicze: żeby było wiadomo „co jest czym”

Wystarczy kilka prostych zasad:

  • Prefix roli: SRC_, STG_, INT_, PUB_, TST_.
  • Testy „przy stole”: TST_<nazwa_tabeli>__<>zakres> (np. zakres: In/Out/PrePublish).
  • Funkcje testujące: fnAssert* (np. fnAssertNotNull) — nawet jeśli będą rozwijane później, nazwa ma od razu zdradzać intencję.

Klucz: użytkownik po wejściu do edytora zapytań ma od razu widzieć, które obiekty są produkcyjne (PUB), a które diagnostyczne (TST).

6.3. Parametry sterujące: przełączniki do CI/odświeżania

Szablon testów warto kontrolować parametrami, żeby bez zmian w logice móc przełączać tryb działania:

  • pRunTests (true/false) — czy uruchamiać testy w danym odświeżeniu.
  • pFailFast (true/false) — czy przerywać po pierwszym błędzie, czy zbierać pełny raport.
  • pMinSeverity (np. 1–3) — minimalna „waga” naruszeń, które uznajemy za FAIL.
  • pSampleMode (none/rows/percent) — tryb próbkowania danych do szybkich iteracji (przydatne w dev).

Parametry nie muszą być skomplikowane: mają jedynie umożliwić to samo zapytanie uruchamiane w różnych kontekstach (dev vs. produkcja, ręczne odświeżenie vs. harmonogram).

6.4. Jeden „runner” testów: agregacja wyników w tabelę

Centralnym elementem szablonu jest jedno zapytanie typu „runner”, które:

  • zbiera wyniki z pojedynczych testów,
  • normalizuje je do wspólnego formatu (np. TestName, TableName, Status, Severity, ErrorCount, Details),
  • wylicza wynik zbiorczy (PASS/FAIL),
  • opcjonalnie wymusza błąd, jeśli warunki nie są spełnione.

Minimalny format raportu (na poziomie szablonu):

  • TestName — identyfikator testu (czytelny w logach/raporcie)
  • Entity — tabela/obszar danych, którego dotyczy test
  • Status — PASS/FAIL
  • Severity — liczba/kategoria
  • ErrorCount — ile naruszeń wykryto
  • SampleDetails — skrócony opis (np. kilka przykładów kluczy), aby diagnoza była szybka

Przykładowy szkic „runnera” (tylko jako orientacja struktury):

// TST__Runner (szkic)
let
  Tests = {
    // tu odwołania do zapytań/funkcji testowych zwracających tabelę wyników
    TST_Orders__Out,
    TST_Customers__In
  },
  Report = Table.Combine(Tests),
  Failures = Table.SelectRows(Report, each [Status] = "FAIL" and [Severity] >= pMinSeverity),
  ShouldFail = pRunTests and (Table.RowCount(Failures) > 0),
  Output = if ShouldFail and pFailFast then error "Data quality tests failed" else Report
in
  Output

Ważne: „runner” jest jedynym miejscem, które decyduje o PASS/FAIL całego zestawu. Dzięki temu pojedyncze testy mogą być proste i niezależne, a egzekucja spójna.

6.5. Co ładować, a czego nie: praktyczny wariant dla raportów

Najczęściej sprawdza się następujący wariant:

  • PUB_* — ładowane (produkcja).
  • TST__Runner — ładowany (diagnostyka w modelu/raporcie), ewentualnie ukryty.
  • Pojedyncze TST_* — zwykle nieładowane, bo „runner” i tak je agreguje.
  • SRC_/STG_/INT_* — zwykle nieładowane (warstwy techniczne).

Dzięki temu w razie problemu użytkownik ma jedną tabelę z wynikami testów, a model nie jest zaśmiecony technicznymi obiektami.

6.6. Uruchamianie: kiedy testy faktycznie się wykonują

W Power Query testy wykonują się wtedy, gdy odświeża się zapytanie, które ich wymaga. To daje dwa typowe podejścia:

  • Testy jako zależność publikacji — PUB_* zależy od TST__Runner (albo od zapytania „Gate”), więc publikacja nie przejdzie bez testów.
  • Testy równolegle jako diagnostyka — PUB_* odświeża się niezależnie, a TST__Runner służy do monitoringu (mniej restrykcyjne).

Szablon może obsłużyć oba scenariusze poprzez prosty „bramkujący” element po stronie zapytań (zależny od parametrów), bez przebudowy całego modelu.

6.7. Integracja z odświeżaniem: „bramka” jakości przed publikacją

Jeżeli celem jest złapanie błędów przed publikacją, dodaje się cienką warstwę bramkującą między INT/STG a PUB:

  • PUB_* pobiera dane z INT_* (lub STG_*)
  • ale przed zwróceniem danych wymusza wykonanie TST__Runner (zależność),
  • i ewentualnie zatrzymuje odświeżanie (gdy FAIL i pFailFast=true).

W praktyce oznacza to, że odświeżenie datasetu/raportu nie „przepchnie” złych danych, jeśli testy tego zabraniają. Jednocześnie parametr pRunTests pozwala kontrolować, czy takie bramkowanie działa zawsze, czy tylko w wybranych cyklach (np. w odświeżeniach produkcyjnych).

6.8. Minimalny zestaw obiektów do skopiowania między projektami

Jako przenośny „starter pack” wystarczy:

  • Parametry: pRunTests, pFailFast, pMinSeverity (opcjonalnie pSampleMode).
  • Jedno zapytanie: TST__Runner (agregacja i wynik zbiorczy).
  • Zestaw zapytań testowych TST_* przypiętych do kluczowych tabel (wejście/wyjście).
  • (Opcjonalnie) zapytanie „Gate”, jeśli chcesz wymusić zależność testów od publikacji.

Taki szablon jest na tyle lekki, że nie narzuca konkretnej implementacji asercji, a jednocześnie tworzy spójny standard uruchamiania i raportowania, który da się konsekwentnie stosować w wielu modelach.

7. Dobre praktyki i pułapki: wydajność, foldowanie zapytań, testowanie na próbkach, utrzymanie słowników i regresja

Testy jakości danych w Power Query potrafią uratować publikację, ale równie łatwo mogą spowolnić odświeżanie, „zepsuć” foldowanie albo dać fałszywe poczucie bezpieczeństwa, jeśli są uruchamiane na nieadekwatnych próbkach. Poniżej zestaw praktyk, które pomagają utrzymać testy jako realne zabezpieczenie, a nie kosztowny rytuał.

Wydajność: testy mają być tanie, a nie „drugi raz to samo”

  • Unikaj podwójnego skanowania danych: najczęstsza pułapka to sytuacja, w której te same ciężkie kroki (łączenia, grupowania, transformacje) są wykonywane osobno dla danych i osobno dla testów. W efekcie odświeżanie trwa niemal 2× dłużej.
  • Testuj możliwie wcześnie i możliwie prosto: część testów da się wykonać zanim dane przejdą kosztowne kroki (np. zanim dojdzie do rozbudowanych joinów). Wczesne wykrycie problemu redukuje koszt obliczeń.
  • Uważaj na kroki, które „materializują” wynik: niektóre operacje w praktyce zmuszają silnik do wykonania pracy natychmiast i w całości (np. niektóre sortowania, buforowanie, nadmierne iteracje po wierszach). Stosuj je oszczędnie i celowo.
  • Minimalizuj zakres w testach: jeżeli test dotyczy jednej kolumny lub jednego warunku, nie wciągaj do obliczeń pełnej tabeli i wszystkich kolumn — to częsty, niewidoczny koszt.
  • Kontroluj liczbę testów i ich ciężar: 12 asercji nie musi oznaczać 12 drogich przebiegów po danych. Dąż do tego, by większość testów była lekka, a ciężkie były nieliczne i dobrze uzasadnione.

Foldowanie zapytań: kiedy testy pomagają, a kiedy przeszkadzają

Foldowanie (query folding) jest krytyczne, gdy źródłem jest baza danych lub inny silnik, który potrafi wykonać filtracje i agregacje „u siebie”. Pułapka polega na tym, że niektóre konstrukcje testów w Power Query mogą przerwać foldowanie, przez co całość danych zostanie ściągnięta lokalnie i dopiero wtedy przetestowana.

  • Trzymaj testy jak najbliżej źródła: test, który daje się wyrazić jako filtr, agregacja, sprawdzenie nulli czy unikalności, często może zostać „zepchnięty” do źródła i wykonać się szybciej.
  • Uważaj na „nieprzetłumaczalne” kroki: niestandardowe funkcje, złożone operacje tekstowe i część transformacji potrafią wyłączyć foldowanie. Jeśli test wymaga takich operacji, rozważ jego umiejscowienie tak, aby nie blokował foldowania dla wcześniejszych kroków.
  • Nie zakładaj, że foldowanie zawsze działa: nawet drobna zmiana w logice może zmienić plan wykonania. W praktyce warto traktować foldowanie jako zasób, który łatwo stracić bez zauważenia.

Testowanie na próbkach: użyteczne, ale ryzykowne

Próbkowanie jest kuszące, bo skraca czas odświeżania w fazie rozwoju, ale może ukryć błędy, które ujawnią się dopiero na pełnym wolumenie lub w rzadkich przypadkach brzegowych.

  • Próbki do developmentu, pełne dane do walidacji: próbkuj w trakcie budowy transformacji i testów, ale zapewnij etap, w którym testy przechodzą na pełnym zbiorze przed publikacją.
  • Uważaj na stronniczość próbek: pierwsze N wierszy, pojedynczy dzień, jeden region lub jedna kategoria często daje „ładne” dane. Losowość i pokrycie wariantów są ważniejsze niż wygoda.
  • Testy zależne od rozkładu danych: asercje oparte o progi, częstości czy kompletność mogą zachowywać się inaczej na próbce niż na całości. Dla takich testów próbka jest szczególnie zdradliwa.

Utrzymanie słowników i reguł: stabilność kontra dryf definicji

W praktyce jakość danych rzadko psuje się „nagle”; częściej występuje dryf: zmieniają się kody, dozwolone wartości, mapowania, zakresy dat, logika biznesowa. Bez higieny słowników testy będą albo ciągle fałszywie alarmować, albo przestaną chronić przed realnym błędem.

  • Oddziel reguły od danych: listy dozwolonych wartości, mapowania i definicje progów powinny być zarządzane jako osobne artefakty (np. parametry, tabele referencyjne), a nie „zaszyte” w wielu miejscach.
  • Jedno źródło prawdy: jeśli ten sam słownik pojawia się w kilku raportach lub przepływach, rozjazdy są kwestią czasu. Lepiej mieć centralnie utrzymywane definicje niż kopiowane fragmenty.
  • Wersjonowanie zmian: nawet proste notowanie kiedy i dlaczego zmienił się słownik pomaga rozróżnić błąd danych od legalnej zmiany biznesowej.
  • Ostrożnie z „naprawami w locie”: automatyczne mapowanie nieznanych wartości do „Inne” może ukryć problem. Testy powinny wykrywać nowe wartości, a decyzja o ich akceptacji powinna być świadoma.

Regresja: testy mają wykrywać zmiany, które psują wynik

Najbardziej kosztowne błędy to te, które wchodzą niezauważone po zmianie źródła, logiki transformacji albo po aktualizacji modelu. Testy jakości danych mogą pełnić rolę „siatki bezpieczeństwa”, ale tylko jeśli są odporne na zmiany pozorne i wrażliwe na zmiany istotne.

  • Stabilne warunki, a nie kruche oczekiwania: testy oparte o dokładne liczby wierszy czy stałe rozkłady często są zbyt kruche. Lepsze są warunki, które odzwierciedlają reguły (np. brak duplikatów klucza, kompletność krytycznych pól, spójność relacji).
  • Progi i tolerancje tam, gdzie to ma sens: niektóre metryki naturalnie falują (np. liczba rekordów dziennie). Zamiast twardych wartości lepiej stosować zakresy akceptacji, by nie generować „szumu”.
  • Rozróżniaj błąd danych od zmiany definicji: jeśli definicja biznesowa się zmienia, testy powinny zostać zaktualizowane wraz z nią. Brak takiej synchronizacji prowadzi do gaszenia alarmów zamiast poprawy jakości.
  • Powtarzalność uruchomień: testy powinny dawać ten sam wynik przy tych samych danych. Jeśli wynik testu zależy od czasu uruchomienia, przypadkowej kolejności lub niestabilnych identyfikatorów, regresja staje się trudna do oceny.

Typowe pułapki organizacyjne

  • „Testy są, więc jesteśmy bezpieczni”: testy pokrywają tylko to, co zostało świadomie sprawdzone. Brak testu na krytyczną regułę jest równie ryzykowny jak brak testów w ogóle.
  • Zbyt wiele alarmów: jeśli testy często „krzyczą” bez realnego problemu, użytkownicy przestają je traktować poważnie. Lepiej mieć mniej testów, ale trafnych, niż dużo i hałaśliwych.
  • Brak właściciela reguł: gdy nie wiadomo, kto decyduje o akceptacji odstępstw, testy nie zamieniają się w działanie. Reguły jakości muszą mieć właściciela po stronie biznesu lub data governance.
  • Testy jako kosmetyka po fakcie: dodawanie testów dopiero po incydencie jest lepsze niż nic, ale najwyższą wartość dają wtedy, gdy są częścią standardu przygotowania danych do publikacji.

Dobrze zaprojektowane testy jakości w Power Query są możliwe do utrzymania i szybkie, o ile pilnujesz ich kosztu obliczeniowego, chronisz foldowanie, rozsądnie używasz próbek oraz traktujesz słowniki i progi jako żywe elementy systemu. Najważniejsze jest to, by testy wspierały decyzje i zapobiegały regresjom, a nie generowały dodatkową niepewność.

💡 Pro tip: Pilnuj, by testy nie dublowały ciężkich kroków i nie psuły query folding — wykonuj je jak najbliżej źródła, na minimalnym zestawie kolumn i unikaj „nieprzetłumaczalnych” funkcji w krytycznych miejscach. Próbki stosuj tylko w developmentcie (z losowością i pokryciem wariantów), a słowniki/progi trzymaj jako jedno wersjonowane źródło prawdy, żeby regresje łapać bez zalewu fałszywych alarmów.

Typowe problemy i diagnostyka: błędy uruchomienia, brakujące pakiety, konflikty typów, problemy z odświeżaniem i renderowaniem wykresów

Nawet najlepiej zaprojektowane testy jakości danych w Power Query potrafią „wywrócić” odświeżanie z powodów, które nie mają nic wspólnego z samą logiką asercji. W praktyce diagnostyka sprowadza się do szybkiego rozpoznania, czy problem dotyczy środowiska (konektory, uprawnienia, gateway), danych (zmieniony schemat, nowe wartości, brak kolumn), czy modelu (typy, relacje, formaty, wizualizacje). Poniżej zebrane są najczęstsze klasy problemów oraz sposoby, jak je odróżnić i gdzie szukać przyczyny.

1) Błędy uruchomienia zapytań: co jest „awarią”, a co wynikiem testu

Test jakości danych powinien dawać przewidywalny wynik (np. „pass/fail”), ale Power Query bywa bezlitosne dla błędów wykonania. Kluczowa różnica: błąd uruchomienia przerywa odświeżanie i zwykle wynika z niedostępności zasobu lub złamania założeń technicznych; negatywny wynik testu to kontrolowana informacja o jakości danych, którą da się raportować.

  • Niedostępne źródło danych (URL, plik, baza): objawia się błędami połączenia, timeoutami, odmową dostępu. Weryfikuj: czy źródło działa, czy ścieżka/endpoint się nie zmieniły, czy brama (gateway) jest online.
  • Zmiana schematu (usunięta/zmieniona nazwa kolumny): błąd pojawia się w krokach odwołujących się do konkretnej kolumny. Diagnoza: sprawdź pierwszy krok, w którym używana jest brakująca kolumna lub następuje wybór/zmiana typu.
  • Nieoczekiwane wartości (np. tekst w kolumnie liczbowej): często wychodzi przy konwersji typu lub transformacjach wymagających poprawnego formatu. To częsty sygnał, że testy powinny poprzedzać kroki „wymuszające” typy.

2) „Brakujące pakiety” i ograniczenia środowiska: konektory, funkcje, polityki

Power Query nie działa jak środowisko z menedżerem pakietów, ale w praktyce spotyka się problemy przypominające „brak dependencji”: brak dostępu do konektora, różnice między Power BI Desktop a usługą, zablokowane źródła lub funkcje zależne od polityk organizacyjnych.

  • Niedostępny konektor lub inna wersja silnika: to samo zapytanie może działać lokalnie, a failować w usłudze lub na gateway. Diagnozuj poprzez porównanie środowisk: Desktop vs Service vs Dataflow, oraz zgodność bramy i sterowników.
  • Ograniczenia prywatności i poziomy bezpieczeństwa: mieszanie źródeł (np. plik + baza) może skutkować blokadą lub innym planem wykonania. Jeśli problem znika po zmianie ustawień prywatności, to znak, że winne są reguły izolacji danych, nie testy.
  • Uwierzytelnianie: wygasłe tokeny, zmienione metody logowania, brak uprawnień do zasobów. Typowy objaw: odświeżanie działało „wczoraj”, dziś przestaje bez zmian w zapytaniach.

3) Konflikty typów i formatów: kiedy „te same” dane przestają być tymi samymi

Konflikty typów są jedną z najczęstszych przyczyn błędów po wdrożeniu. Źródła danych potrafią zmieniać typy (np. liczba → tekst), a Power Query może automatycznie dodać kroki zmiany typu, które później stają się kruche. Dodatkowo dochodzą różnice lokalizacji: separator dziesiętny, format dat, strefy czasowe.

  • Automatyczne wykrywanie typów: czasem pomaga, czasem psuje stabilność. Jeśli po odświeżeniu pojawiają się błędy konwersji, sprawdź, czy typy nie zostały narzucone zbyt wcześnie lub zbyt „twardo”.
  • Daty i godziny: różnice między Date/DateTime/DateTimeZone mogą prowadzić do przesunięć i błędnych agregacji, a w konsekwencji do „fałszywych alarmów” w testach.
  • Null vs pusty tekst: wiele źródeł rozróżnia te przypadki inaczej niż oczekujesz. Skutkuje to rozjazdami w filtrach, łączeniach i licznikach rekordów.

4) Problemy z odświeżaniem: wydajność, foldowanie i „zawieszające” się kroki

Testy jakości danych zwiększają liczbę transformacji, więc potrafią ujawnić problemy wydajnościowe. W diagnostyce ważne jest rozpoznanie, czy zapytanie przestało się odświeżać przez czas wykonania, brak foldowania (przeniesienia obliczeń do źródła), czy przez zbyt ciężkie operacje na dużych wolumenach.

  • Spadek wydajności po dodaniu testów: często wynika z tego, że kroki testowe wymuszają materializację danych lokalnie. Jeśli odświeżanie nagle trwa wielokrotnie dłużej, poszukaj miejsc, gdzie zapytanie przestaje się składać do źródła.
  • Timeouty i limity: usługa Power BI, bramy oraz źródła danych mogą mieć limity czasu lub zasobów. Objaw: odświeżanie kończy się błędem po stałym czasie, mimo że zapytanie „coś robi”.
  • Różnice Desktop vs Service: to, co działa w Desktop, może nie przejść w usłudze przez inne zasady poświadczeń, sieć, bramę lub konfigurację prywatności.

5) Błędy po stronie modelu i wizualizacji: gdy testy są OK, a wykresy „nie wyglądają”

Niektóre problemy użytkownicy mylą z błędami danych, choć wynikają z modelowania lub renderowania wizualizacji. Testy mogą potwierdzić, że dane spełniają reguły, a mimo to raport pokazuje „dziury”, zera albo brak słupków.

  • Relacje i kierunek filtrowania: poprawne dane mogą nie docierać do miar, jeśli relacje są nieaktywne, wiele-do-wielu działa inaczej niż zakładano albo filtrowanie jest jednostronne.
  • Formaty i typy w modelu: liczby zapisane jako tekst w modelu nie agregują się jak liczby; daty bez poprawnej tabeli dat potrafią psuć osie czasu i sortowanie.
  • Puste kategorie i brakujące wartości na osi: wizualizacje mogą ukrywać kategorie bez danych lub traktować null inaczej niż oczekujesz. To bywa interpretowane jako „utrata rekordów”, choć to efekt ustawień wykresu.
  • Cache i odświeżanie wizualne: czasem dane są już odświeżone, ale użytkownik widzi stary stan przez buforowanie lub brak pełnego przeładowania elementów raportu.

6) Jak podejść do diagnostyki bez „rozbierania” całego rozwiązania

W praktyce najlepiej działa podejście warstwowe: najpierw ustal, czy problem jest w połączeniu do źródła, potem czy dotyczy transformacji, a dopiero na końcu — czy to kwestia modelu/raportu. W kontekście testów jakości danych szczególnie ważne jest odróżnienie kontrolowanego niepowodzenia testu od niekontrolowanego błędu wykonania, bo te dwa przypadki wymagają zupełnie innej reakcji operacyjnej (raportowanie vs naprawa odświeżania).

Jeśli chcesz poznać więcej takich przykładów, zapraszamy na szkolenia Cognity, gdzie rozwijamy ten temat w praktyce.

Majczęściej zadawane pytania i odpowiedzi odnośnie Power Query: testy jakości danych jako „unit testy” — 12 asercji, które łapią błędy przed publikacją

Czym są testy jakości danych w Power Query i dlaczego autor porównuje je do unit testów?

Testy jakości danych w Power Query to powtarzalne asercje sprawdzające, czy dane po transformacji nadal spełniają ustalone reguły. Są porównywane do unit testów, ponieważ nie oceniają samego uruchomienia kodu, tylko kontrakt danych: kompletność, spójność, unikalność, zakresy wartości i zgodność schematu. Dzięki temu błędy można wychwycić przed publikacją raportu lub załadowaniem modelu.

Jakie błędy najczęściej wykrywają asercje jakości danych w Power Query?

Asercje najczęściej wykrywają ciche błędy, które nie przerywają odświeżania, ale psują wynik analizy. Typowe przykłady to:

  • brakujące lub zmienione kolumny,
  • nulle w polach krytycznych,
  • duplikaty kluczy,
  • wartości spoza słownika lub zakresu,
  • braki dopasowania po joinie,
  • nagły spadek liczby wierszy albo brak świeżych danych.
Kiedy najlepiej uruchamiać testy jakości danych w pipeline ETL lub ELT?

Testy jakości danych najlepiej uruchamiać po ekstrakcji, po kluczowych transformacjach i tuż przed publikacją. Każdy z tych punktów wykrywa inny typ ryzyka. Wcześnie złapiesz zmiany w źródle i schemacie, po drodze sprawdzisz skutki joinów oraz mapowań, a na końcu potwierdzisz, że finalna tabela spełnia kontrakt wymagany przez model lub raport.

Od których asercji warto zacząć, jeśli dopiero wdrażam testy w Power Query?

Na początek najlepiej wdrożyć kilka prostych asercji o dużej wartości diagnostycznej. Najczęściej dobry start dają:

  • sprawdzenie wymaganych kolumn,
  • zgodność typów danych,
  • brak nulli w kolumnach krytycznych,
  • unikalność klucza,
  • test wolumenu i świeżości danych.

Taki zestaw szybko wychwytuje najczęstsze regresje bez rozbudowywania całej architektury testowej.

Jak powinien wyglądać wynik pojedynczej asercji w Power Query?

Wynik pojedynczej asercji powinien mieć stały, prosty kontrakt możliwy do łączenia z innymi testami. W artykule pojawia się podejście, w którym test zwraca informację o nazwie testu, statusie Pass/Fail, liczbie naruszeń, poziomie severity, komunikacie oraz opcjonalnej próbce błędnych wierszy. Taki format ułatwia budowę jednej tabeli wyników i późniejszą automatyczną bramkę jakości.

Czy lepiej zatrzymywać odświeżanie przy błędzie, czy zbierać pełny raport testów?

Najpraktyczniejsze jest rozdzielenie raportowania od decyzji o blokadzie odświeżania. Pełny raport daje lepszą diagnozę, bo pokazuje wszystkie naruszenia w jednym przebiegu. Fail-fast lepiej sprawdza się tam, gdzie publikacja błędnych danych jest niedopuszczalna. Dobrym kompromisem jest raportowanie wszystkich testów i blokowanie tylko błędów o wybranym severity lub po przekroczeniu progów.

Jak zorganizować zapytania i nazwy testów, żeby rozwiązanie było czytelne i łatwe w utrzymaniu?

Najlepiej rozdzielić zapytania na warstwy i stosować spójne prefiksy nazw. W praktyce pomagają osobne obszary dla źródeł, transformacji, publikacji i testów, na przykład SRC_, STG_, PUB_ i TST_. Dzięki temu łatwo odróżnić dane produkcyjne od artefaktów diagnostycznych, szybciej znaleźć testy przypisane do konkretnej tabeli i ograniczyć przypadkowe zależności między logiką biznesową a walidacją.

Na co uważać, żeby testy jakości danych w Power Query nie spowalniały odświeżania?

Największe ryzyko to dublowanie ciężkich kroków i utrata query foldingu. Testy warto wykonywać możliwie blisko źródła, na minimalnym zestawie kolumn i bez niepotrzebnego materializowania danych. W fazie developmentu można używać próbek, ale przed publikacją trzeba uruchomić walidację na pełnym zbiorze. Trzeba też pilnować, by słowniki i progi były utrzymywane osobno, a nie zaszyte w wielu miejscach.

icon

Formularz kontaktowyContact form

Imię *Name
NazwiskoSurname
Adres e-mail *E-mail address
Telefon *Phone number
UwagiComments