Opublikowano:
26.10.2023 - tagi:
Czym są Property Observers? Swift pozwala na obserwowanie ewentualnych zmian przypisywanych do zmiennej. Dzięki property observers możesz zareagować na przypisanie wartości zmiennej tuż przed lub zaraz *po .
Możesz użyć:
willSet — Zostanie wywołane zaraz przed przypisanie nowej wartoścididSet — Zostanie wywołane tuż po przypisaniu wartościSkładania prezentuje się następująco:
var someProperty: Int = 0 {
willSet {
print ("New value: \(newValue) " )
}
didSet {
print ("Old value: \(oldValue) " )
}
}
Zarówno willSet jak i didSet przyjmują domyślnie parametry:
Dla willSet jest to newValue — jest to wartość, która ma zostać przypisana do zmiennej. Dla didSet jest to oldValue — jest to poprzednia wartość, którą zawierała ta zmienna. Możesz też sam określić nazwy tych parametrów:
var someProperty: Int = 0 {
willSet (valueToSet) {
print ("New value: \(valueToSet) " )
}
didSet (previousValue) {
print ("Old value: \(previousValue) " )
}
}
Przykład struct CaloriesCounter {
var totalCalories: Int = 0 {
willSet (newTotalCalories) {
print ("Total calories is \(newTotalCalories) " )
}
didSet {
if totalCalories > oldValue {
print ("Added \(totalCalories - oldValue) calories" )
}
}
}
}
Warto wiedzieć Jeśli oznaczysz zmienną jako property observer :
Nie może to być stała let , tylko var . Musi zawierać wartość domyślną. Jeśli nie wiesz, co przypisać przypisz nil . Możesz też oznaczyć zmienną jako optional — wtedy domyślnie zostanie przypisane nil do zmiennej. Nie możesz użyć tego wspólnie z computed property . Ponieważ computed property nie przechowuje wartości. Nie musisz używać willSet i didSet razem. Jeśli potrzebujesz tylko jednej z nich, użyj tylko tej. Opublikowano:
19.10.2023 - tagi:
Czytelność testów "Kod częściej się czyta, niż pisze", "Kod nie jest zapisany w skale". Te spostrzeżenia odnoszą się także do testów. W końcu testy to też kod!
W takim razie warto zastanowić się, jak można zwiększyć ich czytelność?
Nie tylko czytelność. Jak pisać testy łatwiejsze w utrzymaniu?
Lepsze testy — krok po kroku Zacznijmy od przykładu.
Komponent:
export function MyComponent ( ) {
const [state, setState] = useState (0 );
return (
<>
<div>
{state}
</div>
<div>
<button onClick={() => setState((prevState: number) => prevState + 1)}>Increment</button>
<button onClick={() => setState((prevState: number) => prevState - 1)}>Decrement</button>
</div>
</>
)
}
Lepsze testy: Krok 1 const { getByRole, getByText } = screen;
test ('should increment counter' , async () => {
render (<MyComponent />);
const btnIncrement = getByRole ('button' , { name : "Increment" });
await userEvent.click (btnIncrement);
expect (getByText ('1' )).toBeInTheDocument ();
});
test ('should decrement counter' , async () => {
render (<MyComponent />);
const btnDecrement = getByRole ('button' , { name : "Decrement" });
await userEvent.click (btnDecrement);
expect (getByText ('-1' )).toBeInTheDocument ();
});
Co można zrobić, żeby test był bardziej czytelny?
Lepsze testy: Krok 2 Za każdym razem trzeba wywoływać funkcję: render() .
Wrzućmy ją do funkcji: renderComponent :
const renderComponent = ( ) => render (<MyComponent />);
Refaktoring testu:
test ('should increment counter' , async () => {
renderComponent ();
const btnIncrement = getByRole ('button' , { name : "Increment" });
...
});
test ('should decrement counter' , async () => {
renderComponent ();
const btnDecrement = getByRole ('button' , { name : "Decrement" });
...
});
Jest odrobinę lepiej.
Czas na następny krok.
Lepsze testy: Krok 3 Żeby testować element, potrzebujesz referencji do elementów znajdujących się w komponencie.
Pisanie zapytać w stylu:
const btnIncrement = getByRole ('button' , { name : "Increment" });
Jest męczące.
A co gdyby funkcja renderująca testowany komponent (renderComponent ) zwracała referencje do elementów, które posiada?
Nowa wersja renderComponent :
const renderComponent = ( ) => {
render (<MyComponent />);
const btnIncrement = getByRole ('button' , { name : "Increment" });
const btnDecrement = getByRole ('button' , { name : "Decrement" });
const userClicksOn = async (element ) => await userEvent.click (element);
return {
btnIncrement,
btnDecrement,
userClicksOn
}
}
I zaktualizowane testy:
test ('should increment counter' , async () => {
const { btnIncrement, userClicksOn } = renderComponent ();
await userClicksOn (btnIncrement);
expect (getByText ('1' )).toBeInTheDocument ();
});
test ('should decrement counter' , async () => {
const { btnDecrement, userClicksOn } = renderComponent ();
await userClicksOn (btnDecrement);
expect (getByText ('-1' )).toBeInTheDocument ();
});
Podsumowanie Takie podejście do pisania testów sprawia, że rozwiązujesz kilka problemów:
Centralizacja — Ewentualne zmiany, będziesz robić w funkcji renderującej komponent. Nie musisz "skakać" po wielu testach, żeby coś naprawić. Testy są bardziej czytelne. Opublikowano:
17.10.2023 - tagi:
Jaka kolejność na liście? Można to sprawdzić na kilka sposobów.
Najpierw kod przykładowej aplikacji:
import { useState } from "react" ;
export function MyComponent ( ) {
const [tasks, setTasks] = useState (['Task 1' , 'Task 2' , 'Task 3' ]);
const [task, setTask] = useState ('' );
const onChangeTask = (e ) => {
setTask (e.target .value )
};
const onAddTask = (e ) => {
e.preventDefault ();
setTasks ([task, ...tasks]);
setTask ('' );
}
return (
<>
<form onSubmit={onAddTask}>
<label htmlFor="newTask">Task title:</label>
<input type="text" id="newTask" value={task} onChange={onChangeTask} />
<button>Add</button>
</form>
<ul>
{
tasks.map((task, index) => (
<li key={index}>
{task}
</li>
))
}
</ul>
</>
)
}
Przykłady Sposób 1: toMatchInlineSnapshot Jest udostępnia funkcję toMatchInlineSnapshot testowania struktury DOM.
import { render, screen } from '@testing-library/react' ;
import { MyComponent } from "./MyComponent" ;
test ('should check order of list items' , async () => {
const { getByRole } = screen;
render (<MyComponent />);
const list = getByRole ('list' );
expect (list).toMatchInlineSnapshot (`
<ul>
<li>
Task 1
</li>
<li>
Task 2
</li>
<li>
Task 3
</li>
</ul>
` )
});
Jeśli element listy jest dość rozbudowany takie testowanie może być uciążliwe. Ale można to obejść w prosty sposób!
Wywołaj najpierw test z toMatchInlineSnapshot bez żadnych argumentów:
expect (list).toMatchInlineSnapshot ();
po chwili zostanie dodany argument do toMatchInlineSnapshot z listą elementów.
Sposób 2: Tablica elementów Możesz też pobrać nazwy elementów i umieścić je w tablicy:
import { render, screen } from '@testing-library/react' ;
import { MyComponent } from "./MyComponent" ;
test ('should check order of list items' , async () => {
const { getAllByRole } = screen;
render (<MyComponent />);
const listItems = getAllByRole ('listitem' ).map ((item ) => item.textContent );
expect (listItems).toEqual (['Task 1' , 'Task 2' , 'Task 3' ]);
});
Opublikowano:
14.10.2023 - tagi:
Do czego służy within? React Testing Library udostępnia funkcję o nazwie within , która pozwala na pisanie zapytań tylko w obrębie danego fragmentu z DOM.
Załóżmy, że mamy listę i każdy element listy składa się z innych elementów. Na przykład:
<ul>
<li>
<h2>Item 1</h2>
<button type="button">Edit</button>
<button type="button">Remove</button>
</li>
<li>
<h2>Item 2</h2>
<button type="button">Edit</button>
<button type="button">Remove</button>
</li>
</ul>
Teraz chcemy napisać test, który sprawdzi usuwanie elementu z listy. Jak pobrać referencję do przycisku: "Remove "? Można każdemu przycisku nadać unikalne id za pomocą data-testid .
Można też użyć funkcji within wystarczy, że przekażemy do niej pojedynczy element listy. Następnie za pomocą zapytania zdobyć referencję do przycisku.
Przykład Poniżej znajduje się prosty kod TODO listy.
Komponent:
import {useState} from "react" ;
export function MyComponent ( ) {
const [tasks, setTasks] = useState (['Task 1' , 'Task 2' , 'Task 3' ]);
const [task, setTask] = useState ('' );
const onChangeTask = (e ) => {
setTask (e.target .value )
};
const onAddTask = (e ) => {
e.preventDefault ();
setTasks ([task, ...tasks]);
setTask ('' );
}
const onRemoveTask = (taskToRemove ) => {
setTasks (tasks.filter (currentTask => currentTask != taskToRemove))
}
return (
<>
<form onSubmit={onAddTask}>
<label htmlFor="newTask">Task title:</label>
<input type="text" id="newTask" value={task} onChange={onChangeTask} />
<button>Add</button>
</form>
<ul>
{
tasks.map((task, index) => (
<li key={index}>
<span data-testid="name">{task}</span>
<button type="button" onClick={() => onRemoveTask(task)}>Remove</button>
</li>
))
}
</ul>
</>
)
}
I chcesz stestować usuwanie zadania (element listy).
Test:
import {render, screen, within} from '@testing-library/react' ;
import userEvent from '@testing-library/user-event' ;
import {MyComponent } from "./MyComponent" ;
test ('should remove task from list' , async () => {
const { getAllByRole, queryByText } = screen;
render (<MyComponent />);
const listItems = getAllByRole ('listitem' ).map ((item ) => ({
name : within (item).getByTestId ('name' ).textContent ,
removeBtn : within (item).getByRole ('button' , { name : 'Remove' }),
}));
const removeTaskBtn = listItems.find ((item ) => item.name === 'Task 1' ).removeBtn ;
await userEvent.click (removeTaskBtn);
expect (queryByText ('Task 1' )).not .toBeInTheDocument ();
});
Najpierw iterujemy po wszystkich elementach listy za pomocą getAllByRole . Tworzona jest tablica, która zawiera nazwę elementu i referencję do przycisku do usuwania zadania.
Przy każdej iteracji korzystamy z funkcji within . Przekazywany jest do niej pojedynczy element listy. Dzięki temu możesz napisać zapytanie: Pobierz mi przycisk o nazwie "Remove" . Nie ważne, że na liście znajduje się więcej niż jeden taki przycisk o tej samej nazwie!
Opublikowano:
10.10.2023 - tagi:
N pierwszych elementów Mamy tablicę:
const items = [1 , 2 , 3 , 4 , 5 ];
I potrzebujesz pobrać trzy pierwsze elementy tablicy.
Jak można to zrobić?
Sposób 1: slice const itemsB = items.slice (0 , 3 );
console .log (itemsB);
Sposób 2: length items.length = 3 ;
console .log (items);
Nowsze wpisy Poprzednie wpisy