Matteo Manferdini

Az egységtesztelés sok fejlesztő számára zavaros. Ezt tovább rontják a fejlett technikák, amelyekre az osztályok vagy az aszinkron kód teszteléséhez van szükség, mint például a hálózati kérések esetében.

A valóságban az egységtesztelés alapját egyszerű alapfogalmak képezik. Ha ezeket megértettük, az egységtesztelés hirtelen megközelíthetőbbé válik.

SwiftUI-alkalmazások architektúrája MVC-vel és MVVM-mel

KAPJA MEG AZ INGYENES KÖNYVET MOST

Tartalom

  • Egységtesztelés egyszerűen elmagyarázva, érthetően
  • Az egységtesztelés lehetővé teszi a nehezen elérhető szélsőséges esetek ellenőrzését és a hibák megelőzését
  • Egységtesztek írása Xcode-ban
  • A jó architektúra megkönnyíti a kód tesztelését
  • Az értéktípusok tiszta függvényként való kezelése az egységtesztelés egyszerűsítése érdekében
  • Kódjának ellenőrzése az XCTest keretrendszer assertions függvényeinek használatával
  • Egy teszt kódlefedettségének mérése Xcode-ban
  • Miért értelmetlen a konkrét kódlefedettségre való törekvés, és mit tegyen helyette

Egységtesztelés egyszerűen elmagyarázva, érthetően

Amikor alkalmazást írsz, végigmész egy cikluson, amelyben kódot írsz, futtatod, és ellenőrzöd, hogy az a kívánt módon működik-e. De ahogy a kódod növekszik, egyre nehezebb lesz manuálisan tesztelni a teljes alkalmazásodat.

Az automatizált tesztelés elvégezheti ezeket a teszteket Ön helyett. Gyakorlatilag tehát az egységtesztelés olyan kód, amely biztosítja, hogy az alkalmazás kódja helyes legyen.

A másik ok, amiért az egységtesztelést nehéz lehet megérteni, az az, hogy sok fogalom veszi körül: tesztelés az Xcode-ban, az alkalmazás architektúrája, tesztpárosítások, függőségi injektálás és így tovább.

A lényegét tekintve azonban az egységtesztelés gondolata meglehetősen egyszerű. Egyszerű egységteszteket írhatsz egyszerű Swiftben. Nincs szükséged semmilyen speciális Xcode funkcióra vagy kifinomult technikára, és akár egy Swift Playgroundon belül is futtathatod őket.

Nézzünk egy példát. Az alábbi függvény kiszámítja egy egész szám faktoriálisát, ami a bemenetnél kisebb vagy azzal egyenlő pozitív egész számok szorzatát jelenti.

1
2
3
4
5
6
7

func factorial(of number: Int) -> Int {
var result = 1
for factor in 1…number {
result = result * factor
}
return result
}

Ahhoz, hogy ezt a függvényt kézzel teszteljük, megetetünk vele néhány számot, és ellenőrizzük, hogy az eredmény megfelel-e az elvárásainknak. Így például négy számmal próbálnád ki, és ellenőriznéd, hogy az eredmény 24.

Azt automatizálhatod, ha írsz egy Swift függvényt, amely elvégzi helyetted a tesztet. Például:

1
2
3
4
5

func testFactorial() {
if factorial(of: 4) != 24 {
print(“A 3 faktoriálisa rossz”)
}
}
}

Ez egy egyszerű Swift kód, amit mindenki megérthet. Mindössze annyit tesz, hogy ellenőrzi a függvény kimenetét, és kiír egy üzenetet, ha az nem megfelelő.

Ez az egységtesztelés dióhéjban. Minden más az Xcode által hozzáadott csengő és síp, hogy egyszerűbbé tegye a tesztelést.

Vagy legalábbis ez lenne a helyzet, ha az alkalmazásainkban lévő kód olyan egyszerű lenne, mint az imént írt faktoriális függvény. Ezért van szükségünk az Xcode támogatására és az olyan fejlett technikákra, mint a tesztpárosítás.

Mindemellett ez az ötlet egyszerű és összetett tesztek esetén egyaránt hasznos.

A unit tesztelés lehetővé teszi a nehezen elérhető éles esetek ellenőrzését és a hibák megelőzését

A unit tesztelésnek számos előnye lehet.

A legnyilvánvalóbb, hogy nem kell folyamatosan kézzel tesztelnünk a kódunkat. Fárasztó lehet futtatni az alkalmazásodat, és eljutni egy adott helyre a tesztelni kívánt funkcionalitással.

De ez még nem minden. Az egységtesztek olyan peremes esetek tesztelését is lehetővé teszik, amelyeket egy futó alkalmazásban esetleg nehéz lenne létrehozni. És az éles esetek azok, ahol a legtöbb hiba él.

Mi történik például, ha nullát vagy negatív számot táplálunk a fenti factorial(of:) függvényünkbe?

1
2
3
4
5

func testFactorialOfZero() {
if factorial(of: 0) != 1 {
print(“A 0 faktoriálisa rossz”)
}
}
}

A kódunk megszakad:

Egy valós alkalmazásban ez a kód összeomlást okozna. A for ciklusban a nulla kívül esik a tartományon.

De a mi kódunk nem a Swift tartományok korlátozása miatt hibásodik meg. Azért nem sikerül, mert elfelejtettük figyelembe venni a nem pozitív egész számokat a kódunkban. Definíció szerint negatív egész szám faktoriálisa nem létezik, míg a 0 faktoriálisa 1.

1
2
3
4
5
6
7
8
9
10
11
12
13

func factorial(of number: Int) -> Int? {
if (szám < 0) {
return nil
}
if (szám == 0) {
return 1
}
var result = 1
for factor in 1…number {
result = result * factor
}
return result
}

Most újra lefuttathatjuk a tesztjeinket, és még a negatív számok ellenőrzését is hozzáadhatjuk.

1
2
3
4
5

func testFactorialOfNegativeInteger() {
if factorial(of: -1) != nil {
print(“The factorial of -1 is wrong”)
}
}
}

Itt látható az egységtesztelés utolsó és véleményem szerint legfontosabb előnye. A korábban írt tesztek biztosítják, hogy amikor frissítjük a factorial(of:) függvényünket, ne törjük meg a meglévő kódját.

Ez kulcsfontosságú az összetett alkalmazásokban, ahol folyamatosan kódot kell hozzáadni, frissíteni és törölni. Az egységtesztelés biztonságot ad abban, hogy a változtatásaink nem törték meg a korábban jól működő kódot.

Egységtesztek írása Xcode-ban

Az egységtesztek megértésével most megnézhetjük az Xcode tesztelési funkcióit.

Az új Xcode projekt létrehozásakor rögtön lehetőségünk nyílik egységtesztek hozzáadására. Példaként egy kalóriák nyomon követésére szolgáló alkalmazást fogok használni. A teljes Xcode projektet itt találod.

Ha bejelölöd ezt a lehetőséget, a sablonprojekted tartalmazni fog egy már beállított tesztcélt. Később bármikor hozzáadhat egyet, de én általában alapértelmezés szerint bejelölöm az opciót. Még ha nem is fogok rögtön teszteket írni, minden be lesz állítva, amikor úgy döntök, hogy megírom.

A tesztcélpont már eleve tartalmaz némi sablonkódot a tesztjeinkhez.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

import XCTest
@testable import Calories
class CaloriesTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. Ez a metódus az osztály minden tesztmetódusának meghívása előtt meghívásra kerül.
}
override func tearDownWithError() throws {
// Tegyük ide a teardown kódot. Ezt a metódust az osztály minden tesztmetódusának meghívása után hívjuk meg.
}
func testExample() throws {
// Ez egy példa egy funkcionális tesztesetre.
// Használjuk az XCTAssert és a kapcsolódó függvényeket annak ellenőrzésére, hogy a tesztek helyes eredményt adnak.
}
func testPerformanceExample() throws {
// Ez egy példa egy teljesítménytesztesetre.
self.measure {
// Ide tesszük azt a kódot, amelynek az idejét mérni szeretnénk.
}
}
}

  • A @testable import Calories sor hozzáférést biztosít a projekted főcéljának összes típusához. Egy Swift modul kódja kívülről csak akkor érhető el, ha minden típus és metódus kifejezetten nyilvánosnak van deklarálva. A @testable kulcsszó lehetővé teszi, hogy megkerülje ezt a korlátozást, és még a privát kódhoz is hozzáférjen.
  • A CaloriesTests osztály egy teszteset, azaz összefüggő tesztek csoportosítása. Egy teszteset osztálynak le kell származnia az XCTestCase-ből.
  • A setUpWithError() metódus lehetővé teszi, hogy egy tesztesetben lévő összes teszt számára szabványos környezetet állítsunk be. Ha a tesztek után takarítást kell végeznünk, akkor a tearDownWithError() metódust használjuk. Ezek a metódusok a teszteset minden egyes tesztje előtt és után futnak. Nem lesz rájuk szükségünk, ezért törölhetjük őket.
  • A testExample() metódus egy olyan teszt, mint amilyet a faktoriális példánkhoz írtunk. A tesztmódszereket tetszés szerint nevezhetjük el, de a teszt előtaggal kell kezdődniük, hogy az Xcode felismerje őket.
  • Végül pedig a testPerformanceExample() megmutatja, hogyan mérhetjük a kódunk teljesítményét. A teljesítménytesztek kevésbé szabványosak, mint a unit tesztek, és csak olyan kritikus kódok esetében használod őket, amelyeknek gyorsnak kell lenniük. Mi sem fogjuk használni ezt a módszert.

Az összes tesztesetét és relatív tesztjét az Xcode Test navigátorában találja listázva.

Új teszteseteket adhat hozzá a projektjéhez, egyszerűen új .swift fájlokat a fentihez hasonló kóddal, de egyszerűbb a Teszt navigátor alján található Hozzáadás menü használatával.

A jó architektúra megkönnyíti a kód tesztelését

Egy valós projektben általában nincsenek olyan laza függvények, mint a faktoriális példánkban. Ehelyett struktúrák, felsorolások és osztályok képviselik az alkalmazás különböző részeit.

Elkerülhetetlen, hogy a kódot egy olyan tervezési minta szerint szervezzük, mint az MVC vagy az MVVM. A jól architektúrázott kód nem csak a kódját teszi jól szervezetté és könnyebben karbantarthatóvá. Könnyebbé teszi az egységtesztelést is.

Ez segít megválaszolni egy gyakori kérdést is: melyik kódot tesztelje először?

A válasz: kezdje a modelltípusokkal.

Ennek több oka is van:

  • Ha az MVC mintát vagy annak valamelyik származékát követi, a modelltípusai tartalmazzák a domain üzleti logikáját. Az egész alkalmazásod az ilyen kód helyességétől függ, így még néhány tesztnek is jelentős hatása van.
  • A modelltípusok gyakran struktúrák, amelyek értéktípusok. Ezeket sokkal könnyebb tesztelni, mint a referenciatípusokat (mindjárt meglátjuk, miért).
  • A referenciatípusok, azaz az osztályok függőségekkel rendelkeznek és mellékhatásokat okoznak. Ahhoz, hogy egységteszteket írjunk rájuk, tesztpéldányokra (dummyk, stubs, fakes, spies és mocks) van szükségünk, és kifinomult technikákat kell használnunk.
  • A kontrollerek (MVC kifejezéssel élve) vagy nézeti modellek (MVVM kifejezéssel élve) gyakran kapcsolódnak erőforrásokhoz, például lemezes tárolóhoz, adatbázishoz, hálózathoz és eszközérzékelőkhöz. Párhuzamos kódot is futtathatnak. Ezek megnehezítik az egységtesztelést.
  • A nézeti kód az, amely a leggyakrabban változik, ami törékeny teszteket eredményez, amelyek könnyen megtörnek. És senki sem szereti javítani a hibás teszteket.

Az értéktípusok tiszta függvényként való kezelése az egységtesztelés egyszerűsítése érdekében

Adjunk tehát néhány modelltípust az alkalmazásunkhoz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

struct FoodItem {
let name:
let caloriesFor100Grams: Int
let grams: Int
}
struct Meal {
private(set) var items: =
var calories: Int {
var calories = 0
for item in items {
calories += (item.caloriesFor100Grams / 100) * item.grams
}
return calories
}
mutating func add(_ item: FoodItem) {
items.append(item)
}
}

A Meal struktúra tartalmaz néhány egyszerű logikát az élelmiszerelemek hozzáadására és a benne lévő ételek összes kalóriájának kiszámítására.

Az egységtesztelés azért kapta ezt a nevet, mert a kódot külön egységekben kell tesztelnünk, minden függőséget megszüntetve (a függőségek együttes tesztelését ehelyett integrációs tesztelésnek hívják). Ilyenkor minden egységet fekete dobozként kezelünk. Megetetünk vele valamilyen bemenetet, és teszteljük a kimenetét, hogy ellenőrizzük, az megfelel-e az elvárásainknak.

A faktoriális példánk esetében ez könnyű volt, mert az egy tiszta függvény volt, azaz olyan függvény, amelynek kimenete csak a bemenettől függ, és ez nem okoz semmilyen mellékhatást.

A Meal típusunk esetében azonban, amely állapotot tartalmaz, ez nem így tűnik.

A calories computed tulajdonság által visszaadott értéknek nincs bemenete. Csak az item tömb tartalmától függ. Az add(_:) metódus ezzel szemben nem ad vissza értéket, és csak az étkezés belső állapotát változtatja meg.

A struktúrák azonban értéktípusok, és tekinthetjük őket tiszta függvényeknek. Mivel nincsenek függőségeik, egy metódus bemenetének tekinthetjük egy struktúra kezdeti állapotát, kimenetének pedig a metódus hívása utáni állapotát.

(Ez az egyik oka annak, hogy miért nem szabad referenciatípusokat értéktípusok belsejébe tenni.)

Kódunk ellenőrzése az XCTest keretrendszer assertions függvényeinek használatával

Most már van némi tesztelendő kódunk, és egy hely az Xcode projektünkben, ahová a teszteket írhatjuk.

Még egy utolsó összetevő hiányzik: egy mód arra, hogy kifejezzük, helyes-e a kódunk vagy sem.

A cikk elején lévő példában egyszerű print utasításokat használtam. Ez nem praktikus valódi egységteszteléshez. Nem akar időt pazarolni a naplók átvizsgálására, hogy azonosítsa, mely tesztek sikertelenek. Olyan módszerre van szükségünk, amely közvetlenül a sikertelen tesztekre mutat.

Az XCTest keretrendszerben találunk egy sor állításfüggvényt, amelyek segítségével az Xcode rámutat a sikertelen tesztekre.

Ezekkel megírhatjuk az első tesztünket.

Amint már említettem, az egységtesztek hasznosak az éles esetek ellenőrzéséhez, ezért kezdjük el ellenőrizni, hogy egy üres ételnek nincs-e kalóriája.

1
2
3
4
5
6

class CaloriesTests: XCTestCase {
func testEmptyMeal() throws {
let meal = Meal()
XCTAssertEqual(meal.calories, 0, “Egy üres étkezésnek 0 kalóriát kell tartalmaznia”)
}
}

Az XCTest egy általános XCTAssert függvényt kínál, amely lehetővé teszi bármilyen feltétel állítását. Ezt bármelyik teszthez használhatod, amit írsz, de jobb, ha lehetőség szerint specializáltabb állításfüggvényeket használsz, mint például az XCTAssertEqual (fent), XCTAssertNil és mások. Ezek jobb hibákat produkálnak az Xcode-ban, mint az XCTAssert.

Egyetlen tesztet futtathatsz, ha a kódszerkesztő ereszében a neve melletti gyémántra kattintasz.

Ha egy teszt sikerül, zöld színű lesz, míg a sikertelen teszteket piros színnel jelöljük. A 0-t a tesztben tetszőleges más számra változtathatja, hogy lássa, hogyan néz ki egy sikertelen teszt.

A teszteset összes tesztjét lefuttathatja, ha az osztály deklarációja melletti gyémántra kattint. A fentebb látott Teszt navigátort is használhatja az egyes tesztek vagy tesztesetek futtatására.

Gyakran szeretné egyszerre futtatni az összes tesztet a projektben, amit gyorsan megtehet a billentyűzet cmd+U billentyűkombinációjának lenyomásával vagy az Xcode Termék menüjének Teszt opciójának kiválasztásával.

Egy teszt kódlefedettségének mérése Xcode-ban

A következő, leggyakoribb kérdés az egységteszteléssel kapcsolatban: a kód mekkora részét érdemes tesztelni?

Ezt általában a kódlefedettséggel mérik, azaz, a tesztek által lefedett kód százalékos aránya. Mielőtt tehát válaszolnánk erre a kérdésre, nézzük meg, hogyan mérhetjük a tesztek kódlefedettségét az Xcode-ban.

Először is, hagynunk kell, hogy az Xcode összegyűjtse a tesztcélunk kódlefedettségét. Kattintson az Xcode eszköztár szegmentált vezérlőjében a projekt nevével ellátott gombra (a leállítás gomb mellett). Ezután válassza a felugró menüben a Séma szerkesztése… parancsot.

Ahol válassza ki a vázlatban a Tesztet, majd lépjen az Opciók lapra. Végül pedig válassza ki a Gather coverage for (Fedettség összegyűjtése) lehetőséget. Hagyhatja az opciót az összes célpontra.

Most újra lefuttathatja a tesztjeit, ami lehetővé teszi az Xcode számára a lefedettségi adatok összegyűjtését. Az eredményt a Jelentés navigátorban találja meg, ha kiválasztja a Kódlefedettség elemet a vázlatban.

Itt ellenőrizheti a tesztjei által lefedett kód százalékos arányát a teljes projektre és az egyes fájlokra vonatkozóan.

A számok a mi példánk esetében nem túl jelentősek, mivel alig írtunk kódot. Mégis láthatjuk, hogy a Meal típus add(_:) metódusa 0%-os lefedettséggel rendelkezik, ami azt jelenti, hogy még nem teszteltük.

A calories computed tulajdonság ehelyett 85,7%-os lefedettséggel rendelkezik, ami azt jelenti, hogy vannak olyan végrehajtási utak a kódunkban, amelyeket a tesztünk nem váltott ki.

A mi egyszerű példánkban könnyű megérteni, hogy melyik út az. Csak az üres étkezés kalóriáit teszteltük, tehát a for ciklusban lévő kód nem futott le.

A bonyolultabb módszereknél azonban ez nem biztos, hogy ilyen egyszerű.

Ezért az Xcode szerkesztőben előhívhatjuk a kódlefedettségi csíkot. Ezt a jobb felső sarokban található Adjust Editor Options menüben találja.

Ezzel megjelenik egy csík, amely megmutatja az egyes kódsorok lefedettségét.

A számok azt mutatják, hogy az egyes sorokat hányszor hajtották végre a tesztelés során. Pirossal láthatod, hogy mely sorokat egyáltalán nem futtatták (teljes) vagy csak részben hajtották végre (csíkos).

Miért értelmetlen egy adott kódlefedettségre törekedni, és mit kellene tenned helyette

Szóval, hány százalék a jó kódlefedettségi százalék?

A vélemények nagyon eltérőek. Néhány fejlesztő úgy gondolja, hogy 20% elég. Mások magasabb számokat, például 70%-ot vagy 80%-ot szeretnének. Vannak olyan fejlesztők is, akik szerint csak a 100% az elfogadható.

Véleményem szerint a kódlefedettség értelmetlen mérőszám. Használhatod a teszteléssel kapcsolatos döntéseidhez, de nem szabad úgy kezelned, mint egy célt, amit el kell érned.

Hogy lássuk, miért, írjunk egy újabb tesztet, hogy lefedje azt a kódot, amit még nem teszteltünk.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class CaloriesTests:
let banana = FoodItem(name: “Banán”, caloriesFor100Grams: 89, grams: 118)
let steak = FoodItem(name: “Steak”, caloriesFor100Grams: 271, grams: 225)
let goatCheese = FoodItem(name: “Goat Cheese”, caloriesFor100Grams: 364, gramm: 28)
func testEmptyMeal() throws {
let meal = Meal()
XCTAssertEqual(meal.calories, 0, “Egy üres étkezésnek 0 kalóriát kell tartalmaznia”)
}
func testCalories() {
var meal = Meal()
meal.add(banán)
meal.add(steak)
meal.add(kecskesajt)
XCTAssertEqual(meal.items.count, 3)
XCTAssertEqual(meal.calories, 534)
} }
}

Ha újra lefuttatjuk az összes tesztet, akkor a Model.swift fájl 100%-os lefedettséget kap. Jól néz ki, igaz?

Most menj, és távolítsd el a testEmptyMeal() metódust. Ha a megmaradt teszteket egyedül futtatod, látni fogod, hogy a Meal típusaink lefedettsége még mindig 100%.

Ez már mutatja, hogy a 100%-os szám hamis biztonságérzetet adhat. Tudja, hogy most nem teszteljük az összes szélső esetet a kalória kiszámított tulajdonságára. Mégsem tükrözi ezt a tesztlefedettségünk.

Hozzátenném, hogy a 100%-os lefedettség nemcsak félrevezető, hanem még káros is. Néhány kód a projektedben állandó változásra hajlamos, különösen a legújabb kód, és különösen a nézetekben.

A 100%-os lefedés azt jelenti, hogy csak törékeny teszteket fogsz gyártani, amelyek folyamatosan törnek, és neked kell javítanod. Ezek a tesztek nem fedezik fel a hibákat. Azért törnek meg, mert a kódod funkcionalitása megváltozik.

És itt elérkeztünk ahhoz a ponthoz, amiről senki sem beszél.

Teszteket írni nem szórakoztató, annak ellenére, amit egyes fejlesztők állítanak. A tesztek javítása még kevésbé szórakoztató. Ha túl sokat csinálod, megutálod az egységtesztelést, és teljesen félre fogod tolni.

A pszichológia ugyanolyan fontos a szoftverfejlesztéshez, mint a legjobb gyakorlatok. Nem gépek vagyunk.

Szóval, milyen számot kell megcéloznod? Ez egy hosszabb megbeszélést igényel, de itt összefoglalom a véleményemet.

Írd meg a hasznos teszteket, kezdve a projekted legrégebbi kódjával, ami nem fog változni. A kódlefedettséget csak arra használd, hogy azonosítsd, milyen kódot nem teszteltél még, de ne használd célként vagy annak eldöntésére, hogy mikor írj több tesztet. A 20%-os lefedettség jobb, mint a 80%-os, ha a tesztek értelmesek, és a megfelelő kódot teszteli.

Amint fentebb említettem, a modelltípusok tartalmazzák a legkritikusabb kódot a projektben, amely általában a legkevésbé változik. Kezdje tehát ott.

Általában nem vesződöm azzal, hogy minden egyes végrehajtási útvonalat teszteljek. Például egy valós projektben nem írnám meg a fentebb bemutatott testEmptyMeal() metódust. Igen, kulcsfontosságú az edge case-ek tesztelése, de ezek sem mind fontosak.

Én jellemzően csak a testCalories() tesztet írnám meg. Ez azt mondja meg, hogy a kódom működik, és figyelmeztet, ha később hibát követek el, amikor megváltoztatom ezt a metódust.

Kétségtelen, hogy nem terjed ki minden útra, de ez a kód annyira egyszerű, hogy az csak időpocsékolás lenne. Az időd arra fordítod, hogy valódi kódot írj olyan funkciókhoz, amelyek segítik a felhasználóidat, sokkal fontosabb, mint minden végrehajtási útvonal tesztelése.

Tudom, hogy néhány fejlesztőtől kapni fogok némi faksznit ezért a véleményemért, de nem érdekel. A tesztlefedettség egyike azoknak a vitáknak, amelyeknek soha nem lesz vége.

A kód, amit írsz, általában többnyire helyes. Nem kell paranoiásnak lenni. Ha rengeteg teszted van, ami minden egyes alkalommal elromlik, amikor bármit megváltoztatsz, az teher, nem pedig előny.

Az időd és az akaraterőd korlátozott. Töltsd őket fontosabb feladatokra.

SwiftUI-alkalmazások architektúrája MVC-vel és MVVM-mel

Egyszerű alkalmazást készíteni néhány kód összedobásával. Legjobb gyakorlatok és robusztus architektúra nélkül azonban hamarosan kezelhetetlen spagettikódot kapunk. Ebben az útmutatóban megmutatom, hogyan strukturálhatod megfelelően a SwiftUI-alkalmazásokat.

KAPD MEG AZ INGYENES KÖNYVET MOST

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.