Aplikacja „Pizzeria” – „PASTA la VISTA”

Stwórz aplikację desktopową w technologii WPF, która pozwala użytkownikowi wybierać pizzę, dodawać ją do koszyka, podglądać podsumowanie zamówienia oraz zapisywać paragon do pliku JSON.


Wymagania funkcjonalne

  1. Aplikacja uruchamia się i wyświetla główne okno z dwoma sekcjami: lista produktów (pizza) po lewej oraz koszyk zamówienia po prawej.
  2. Lista produktów zawiera co najmniej 8 różnych pozycji pizzy – nazwa, rozmiar, cena – każdy produkt dostępny w co najmniej jednym rozmiarze.
  3. Przy każdej pozycji lista produktów ma pole wyboru (CheckBox) „wybierz”. Użytkownik może zaznaczyć jedną lub więcej pizz.
  4. Po kliknięciu przycisku „Dodaj zaznaczone” wszystkie zaznaczone produkty zostają dodane do koszyka. Jeżeli pozycja już jest w koszyku, zostaje zwiększona jej ilość.
  5. W koszyku wyświetlany jest spis pozycji z kolumnami: Nazwa, Rozmiar, Cena jednostkowa, Ilość, Suma (ilość×cena).
  6. Przy koszyku dostępne są przyciski: „Usuń zaznaczoną pozycję”, „Wyczyść koszyk”.
  7. Pod listą koszyka wyświetlane jest podsumowanie zamówienia: liczba sztuk („Razem sztuk:”) i łączna kwota („Razem: … zł”).
  8. Użytkownik może kliknąć przycisk „Zapisz paragon (JSON)”. Po kliknięciu:
    • jeśli koszyk jest pusty → wyświetl komunikat ostrzegawczy, nic nie zapisuj;
    • jeśli koszyk zawiera pozycje → zostaje zapisany plik JSON w katalogu użytkownika (np. Dokumenty\Pizzeria\paragon_YYYYMMDD-HHMMSS.json).
  9. Plik JSON zawiera: numer paragonu (np. data-czas), datę wystawienia, lista pozycji (nazwa, rozmiar, cena jednostkowa, ilość, suma dla każdej pozycji), łączną liczbę sztuk i łączną kwotę zamówienia.
  10. Aplikacja posiada stylowanie: ciemne tło całego okna, czytelny kolor tekstu w całej aplikacji z wyjątkiem koszyka – tam tekst powinien być jasnoniebieski. Kolory tła dla sekcji pozostają jak w przykładzie.
  11. W kodzie zastosowana jest struktura folderów i plików projektu:
    • Modele/ModelProdukty.cs
    • Modele/ModelKoszyk.cs
    • Logika/Logika.cs
    • Walidacja/Walidacja.cs
    • MainWindow.xaml + MainWindow.xaml.cs
  12. Dane listy produktów są wypełnione na starcie w kodzie (np. w konstruktorze widoku lub logiki) – uczniowie nie muszą wczytywać z bazy czy pliku.
  13. Binding danych zastosowany – lista produktów i koszyk to kolekcje (np. ObservableCollection), widok (ItemsControl lub ListView) korzysta z ItemsSource.
  14. Przyciski i zmiana ilości w koszyku (przycisk plus/minus) działają poprawnie i natychmiast aktualizują podsumowanie.
  15. Aplikacja działa bez błędów – kompiluje się, uruchamia, funkcje działają jak opisano.

Wymagania niefunkcjonalne

  • Interfejs użytkownika jest czytelny, logicznie rozmieszczony (lista produktów – koszyk – podsumowanie).
  • Kod jest czytelny: nazwy klas/metod zrozumiałe, komentarze tam gdzie to potrzebne.
  • Stylowanie w XAML w miarę spójne (jeden motyw kolorystyczny).
  • Uczniowie wykonują zadanie samodzielnie – użycie gotowych rozwiązań z lekcji jest dozwolone, kopiowanie całości cudzych kodów niedozwolone.
  • Zapis paragonu w JSON odbywa się z użyciem biblioteki System.Text.Json lub innej właściwej, plik zapisany poprawnie i możliwy do odczytu w edytorze tekstowym.

Instrukcja dla ucznia – jak wykonać zadanie

  1. Uruchom Visual Studio → wybierz „Nowy projekt” → „WPF App (.NET)”. Nazwij projekt Pizzeria.
  2. Utwórz w projekcie trzy foldery: Modele, Logika, Walidacja.
  3. W folderze Modele utwórz pliki ModelProdukty.cs i ModelKoszyk.cs zgodnie z opisanymi wcześniej właściwościami.
  4. W folderze Logika utwórz Logika.cs. Zaimplementuj metody: wypełnienie listy produktów, dodawanie do koszyka, usuwanie, czyszczenie, podsumowanie, zapis do pliku JSON.
  5. W folderze Walidacja utwórz Walidacja.cs. Zaimplementuj metodę sprawdzającą, czy koszyk zawiera pozycje przed zapisem.
  6. Otwórz MainWindow.xaml. Zaprojektuj układ w siatce (Grid) z dwoma kolumnami i trzema wierszami (jak w przykładzie). Po lewej lista produktów, po prawej koszyk, na dole przycisk zapisu.
  7. Zdefiniuj zasoby w <Window.Resources> – style dla GroupBox, Button, TextBlock, a także dedykowany styl dla ListView koszyka i ListViewItem z kolorem tekstu jasnoniebieskim (#9AD1FF).
  8. W zakładce XAML przypisz styl Style="{StaticResource KoszykListViewStyle}" oraz ItemContainerStyle="{StaticResource KoszykListViewItemStyle}" do kontrolki ListView koszyka.
  9. W MainWindow.xaml.cs ustaw DataContext = new LogikaAplikacji() lub analogicznie, aby widok mógł bindować właściwości produktów i koszyka.
  10. W widoku po lewej użyj ItemsControl lub ListView do listy produktów z CheckBox (wiąż IsChecked z IsSelected). Po prawej użyj ListView z GridView, kolumny jak w wymaganiach.
  11. Zaimplementuj przyciski:
    • „Dodaj zaznaczone” → metoda dodająca zaznaczone produkty do koszyka.
    • „Usuń zaznaczoną pozycję” → usuwa zaznaczoną pozycję z koszyka.
    • „Wyczyść koszyk” → usuwa wszystkie pozycje po potwierdzeniu.
    • „Zapisz paragon (JSON)” → sprawdza walidację, jeśli OK → zapisuje JSON → pokazuje komunikat („Paragon zapisany: ścieżka”). Jeśli nie OK → pokazuje komunikat ostrzegawczy („Koszyk jest pusty”).
  12. Uruchom aplikację i przetestuj:
    • zaznacz kilka pizz → dodaj → sprawdź koszyk i podsumowanie,
    • zmień ilość (+/-) → sprawdź, że suma się zmienia,
    • usuń pozycję, wyczyść koszyk, próbuj zapisać paragon przy pustym koszyku.
    • zapisz paragon → otwórz plik JSON w Notatniku → sprawdź, czy dane są poprawne (lista pozycji, liczba sztuk, kwota, data).
  13. Spakuj projekt lub prześlij folder z plikiem .sln, .csproj, plikami źródłowymi i plikiem JSON (lub informacją, gdzie został zapisany).

Kryteria oceniania

ObszarPunkty
Uruchomienie aplikacji, brak błędów kompilacji2
Prawidłowa struktura folderów i plików (Modele, Logika, Walidacja)2
Lista produktów + wybór, dodawanie do koszyka2
Koszyk z kolumnami (nazwa, rozmiar, cena, ilość, suma) oraz podsumowanie2
Zapis paragonu do JSON zgodnie z wymaganiami (nazwa pliku, struktura danych)2
Stylowanie: ciemne tło + jasny tekst + jasnoniebieski tekst w koszyku1
Walidacja (np. ostrzeżenie przy pustym koszyku)1
Czytelność kodu i poprawne użycie bindingu1
RAZEM13

Interfejs graficzny aplikacji:

mała podpowiedź z interfejsem.

Instrukcja: Jak wykonać interfejs XAML do aplikacji Pizzeria (WPF)

Stwórz nowy projekt WPF (C#)

W Visual Studio wybierz „Nowy projekt” → „Aplikacja WPF (.NET Core)” lub „Aplikacja WPF (.NET Framework)” (zależnie od wersji środowiska).

Nazwij projekt np. PizzeriaWPF i zapisz go w swoim folderze projektów.

W oknie projektu w Solution Explorerze zobaczysz pliki:
MainWindow.xaml, MainWindow.xaml.cs oraz folder Properties.


Zaprojektuj układ główny okna

W pliku MainWindow.xaml ustaw szerokość i wysokość okna.
W atrybutach <Window> możesz wpisać:

<Window Title="Pizzeria" Height="650" Width="950"
        Background="#0e0f13" Foreground="#e7e7e7" 
        WindowStartupLocation="CenterScreen">

Dodaj główny Grid i podziel okno na dwie kolumny:
lewą (menu) i prawą (koszyk).
Dodatkowo zrób 2–3 wiersze: nagłówek, treść i dolny pasek z przyciskami.

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="2*" />
    <ColumnDefinition Width="2*" />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="*"/>
    <RowDefinition Height="Auto"/>
  </Grid.RowDefinitions>
  <!-- ... -->
</Grid>

Nagłówek serwisu

Dodaj StackPanel, w nim dwa TextBlock: tytuł i podtytuł.

Użyj większej czcionki, koloru i pogrubienia.

Przykładowo:

<StackPanel Grid.ColumnSpan="2" Grid.Row="0">
  <TextBlock Text="Pizzeria – zamówienie online" FontSize="22" FontWeight="Bold"/>
  <TextBlock Text="Zaznacz pizze po lewej, dodaj do koszyka po prawej, a na końcu zapisz paragon."
             Foreground="#a4a8b3"/>
</StackPanel>

Lewy panel: menu pizzy

Dodaj GroupBox z nagłówkiem, a w nim ItemsControl z szablonem pojedynczego produktu.

Każdy produkt ma:
CheckBox – wybór pizzy,
📜 TextBlock – nazwa i rozmiar,
💰 TextBlock – cena.

Użyj stylów do obramowań i tła.

<GroupBox Header="Menu – wybierz pizze" Grid.Row="1" Grid.Column="0">
  <ScrollViewer VerticalScrollBarVisibility="Auto">
    <ItemsControl ItemsSource="{Binding Produkty}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <Border Margin="6" Padding="8" BorderBrush="#232737" BorderThickness="1" Background="#1a1f2b">
            <StackPanel Orientation="Horizontal">
              <CheckBox IsChecked="{Binding IsSelected}" Margin="0,0,10,0"/>
              <StackPanel>
                <TextBlock Text="{Binding Nazwa}" FontSize="14"/>
                <TextBlock Text="{Binding Rozmiar}" FontSize="12" Foreground="#a4a8b3"/>
              </StackPanel>
              <TextBlock Text="{Binding Cena, StringFormat={}{0:0.00} zł}" FontWeight="Bold" Margin="12,0,0,0"/>
            </StackPanel>
          </Border>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
  </ScrollViewer>
</GroupBox>

Prawy panel: koszyk

GroupBox z nagłówkiem.
W środku ListView z kolumnami: nazwa, rozmiar, cena, ilość (edycja +/-), suma.
Skorzystaj z GridView jako widoku.

Nad ListView wstaw przyciski: „Dodaj zaznaczone”, „Usuń zaznaczoną”, „Wyczyść koszyk”.

<GroupBox Header="Koszyk – bieżące zamówienie" Grid.Row="1" Grid.Column="1">
  <StackPanel>
    <StackPanel Orientation="Horizontal">
      <Button Content="➕ Dodaj zaznaczone" Click="BtnDodaj_Click"/>
      <Button Content="❌ Usuń zaznaczoną" Click="BtnUsun_Click"/>
      <Button Content="♻️ Wyczyść koszyk" Click="BtnWyczysc_Click"/>
    </StackPanel>

    <ListView x:Name="ListKoszyk"
              ItemsSource="{Binding Koszyk}"
              SelectedItem="{Binding WybranaPozycja, Mode=TwoWay}">
      <ListView.View>
        <GridView>
          <GridViewColumn Header="Nazwa" DisplayMemberBinding="{Binding Nazwa}" Width="200"/>
          <GridViewColumn Header="Rozmiar" DisplayMemberBinding="{Binding Rozmiar}" Width="80"/>
          <GridViewColumn Header="Cena" DisplayMemberBinding="{Binding CenaJednostkowa}" Width="80"/>
          <GridViewColumn Header="Ilość" Width="80">
            <GridViewColumn.CellTemplate>
              <DataTemplate>
                <StackPanel Orientation="Horizontal">
                  <Button Width="24" Content="-" Tag="{Binding}" Click="BtnMinus_Click"/>
                  <TextBlock Width="28" TextAlignment="Center" Text="{Binding Ilosc}"/>
                  <Button Width="24" Content="+" Tag="{Binding}" Click="BtnPlus_Click"/>
                </StackPanel>
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
          <GridViewColumn Header="Suma" DisplayMemberBinding="{Binding Suma}" Width="90"/>
        </GridView>
      </ListView.View>
    </ListView>
  </StackPanel>
</GroupBox>

Podsumowanie zamówienia

Pod ListView dodaj StackPanel z tekstami:
„Razem sztuk:” i „Razem:”.
Powiąż bindingiem z właściwościami RazemSztuk i RazemKwota.

<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
  <TextBlock Text="Razem sztuk:" FontWeight="Bold"/>
  <TextBlock Text="{Binding RazemSztuk}" Margin="6,0,12,0"/>
  <TextBlock Text="Razem:" FontWeight="Bold"/>
  <TextBlock Text="{Binding RazemKwota, StringFormat={}{0:0.00} zł}" Margin="6,0,0,0"/>
</StackPanel>

Przycisk zapisu paragonu

Na dole okna dodaj przycisk „Zapisz paragon (JSON)” z ToolTipem.

<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
  <Button Content="💾 Zapisz paragon (JSON)" ToolTip="Zapisuje zamówienie do pliku JSON" Click="BtnZapiszParagon_Click"/>
</StackPanel>

Dodaj zasoby stylów do okna

W <Window.Resources> stwórz Style dla:

  • GroupBox – marginesy, obramowanie, tło,
  • Button – marginesy i padding,
  • ListView – kolor tła i czcionki,
  • ListViewItem – styl elementów,
  • TextBlock – zawijanie tekstu.
<Window.Resources>
  <Style TargetType="GroupBox">
    <Setter Property="Margin" Value="8"/>
    <Setter Property="Padding" Value="8"/>
    <Setter Property="BorderBrush" Value="#232737"/>
    <Setter Property="Background" Value="#151824"/>
  </Style>

  <Style TargetType="Button">
    <Setter Property="Margin" Value="6"/>
    <Setter Property="Padding" Value="10,6"/>
  </Style>

  <Style TargetType="ListView">
    <Setter Property="Background" Value="#1a1f2b"/>
    <Setter Property="Foreground" Value="#9AD1FF"/>
  </Style>

  <Style TargetType="ListViewItem">
    <Setter Property="Foreground" Value="#9AD1FF"/>
    <Setter Property="Background" Value="#1a1f2b"/>
  </Style>

  <Style TargetType="TextBlock">
    <Setter Property="TextWrapping" Value="Wrap"/>
  </Style>
</Window.Resources>

Binding i obsługa zdarzeń

Upewnij się, że kolekcje (Produkty, Koszyk) są typu ObservableCollection.
Dzięki temu widok automatycznie odświeży się po dodaniu lub usunięciu elementu.

Ustawienie DataContext:

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
    ZaladujProdukty();
}

Przykładowe klasy:

public class Produkt
{
    public string Nazwa { get; set; } = "";
    public string Rozmiar { get; set; } = "";
    public decimal Cena { get; set; }
    public bool IsSelected { get; set; }
}

public class PozycjaKoszyka : INotifyPropertyChanged
{
    public string Nazwa { get; set; } = "";
    public string Rozmiar { get; set; } = "";
    public decimal CenaJednostkowa { get; set; }
    private int ilosc = 1;
    public int Ilosc { get => ilosc; set { ilosc = value; OnPropertyChanged(nameof(Ilosc)); OnPropertyChanged(nameof(Suma)); } }
    public decimal Suma => CenaJednostkowa * Ilosc;
    public event PropertyChangedEventHandler? PropertyChanged;
    void OnPropertyChanged(string n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
}

Walidacja, komunikaty, dostępność

Dodaj kontrolkę (np. TextBlock) pod koszykiem do wyświetlania komunikatów:

<TextBlock x:Name="TxtInfo" Foreground="#f8d16e" FontStyle="Italic"/>

Wyłączaj przyciski, gdy koszyk jest pusty:

<Button Content="Zapisz paragon" IsEnabled="{Binding Koszyk.Count, Converter={StaticResource IsNotZeroConverter}}"/>

Dodaj ToolTip do przycisków i sprawdź kontrasty – tekst koszyka ma być dobrze widoczny (jasnoniebieski na ciemnym tle).


Zapis paragonu (JSON)

W BtnZapiszParagon_Click zaimplementuj zapis koszyka do pliku:

string json = JsonSerializer.Serialize(Koszyk, new JsonSerializerOptions { WriteIndented = true });
string sciezka = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "paragon.json");
File.WriteAllText(sciezka, json);
MessageBox.Show($"Zapisano paragon: {sciezka}");

Test końcowy

Kliknij „Zapisz paragon” i otwórz plik paragon.json w Dokumentach.
Uruchom aplikację.
Zaznacz kilka pizz i dodaj do koszyka.
Zmień ilość przyciskami +/–.
Sprawdź podsumowanie (sztuki i kwota).

To jeszcze nie koniec

Testy jednostkowe dla „Pizzeria” (MSTest) – krok po kroku

Wymagania wstępne (szybki check)

  • Visual Studio 2022
  • Zainstalowany pakiet „.NET desktop development”
  • Projekt WPF „Pizzeria” kompiluje się bez błędów
  • Logika jest w klasie, którą można utworzyć bez UI (np. Logika/LogikaAplikacji.cs) — u Ciebie jest ✔️

1) Dodaj projekt testowy MSTest

  1. W Solution Explorer kliknij prawym na SolutionAddNew Project…
  2. Wyszukaj: MSTest Test Project (.NET)
  3. Nazwa: Pizzeria.TestsCreate
  4. Po utworzeniu zobaczysz plik UnitTest1.cs (możesz go zostawić lub usunąć — zaraz dodamy swój plik testów).

2) Podepnij projekt testów do projektu Pizzeria

  1. Prawy klik na Pizzeria.TestsAddProject Reference…
  2. Zaznacz PizzeriaOK
  3. Dzięki temu w testach zadziałają przestrzenie nazw: using Pizzeria.Logika; using Pizzeria.Modele;

3) Utwórz plik z testami

  1. Prawy klik na Pizzeria.TestsAddClass…
  2. Nazwa: LogikaAplikacjiTests.cs
  3. Otwórz plik i wklej testy (dopasowane do Twojej LogikaAplikacji):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Pizzeria.Logika;
using System.IO;
using System.Linq;

namespace Pizzeria.Tests
{
    [TestClass]
    public class LogikaAplikacjiTests
    {
        private LogikaAplikacji _logika;

        [TestInitialize]
        public void Setup()
        {
            _logika = new LogikaAplikacji();
        }

        [TestMethod]
        public void DodajDoKoszyka_DlaZaznaczonychProduktow_DodajePozycjeIZerujeIsSelected()
        {
            _logika.Produkty[0].IsSelected = true;   // Margherita
            _logika.Produkty[2].IsSelected = true;   // Pepperoni

            _logika.DodajDoKoszyka();

            Assert.AreEqual(2, _logika.Koszyk.Count);
            Assert.IsFalse(_logika.Produkty[0].IsSelected);
            Assert.IsFalse(_logika.Produkty[2].IsSelected);
        }

        [TestMethod]
        public void DodajDoKoszyka_PonowneDodanieTejSamejPizzy_ZwiekszaIlosc()
        {
            var m = _logika.Produkty.First(p => p.Nazwa == "Margherita");
            m.IsSelected = true; _logika.DodajDoKoszyka(); // 1 szt.
            m.IsSelected = true; _logika.DodajDoKoszyka(); // 2 szt.

            var poz = _logika.Koszyk.First(k => k.Nazwa == "Margherita");
            Assert.AreEqual(2, poz.Ilosc);
        }

        [TestMethod]
        public void UsunPozycje_UsuwaWybranyWierszZKoszyka()
        {
            _logika.Produkty[0].IsSelected = true;
            _logika.Produkty[1].IsSelected = true;
            _logika.DodajDoKoszyka();
            var doUsuniecia = _logika.Koszyk[0];

            _logika.UsunPozycje(doUsuniecia);

            Assert.AreEqual(1, _logika.Koszyk.Count);
        }

        [TestMethod]
        public void WyczyscKoszyk_CzysciWszystko()
        {
            _logika.Produkty[0].IsSelected = true;
            _logika.Produkty[1].IsSelected = true;
            _logika.DodajDoKoszyka();

            _logika.WyczyscKoszyk();

            Assert.AreEqual(0, _logika.Koszyk.Count);
            Assert.AreEqual(0, _logika.RazemSztuk);
            Assert.AreEqual(0.0, _logika.RazemKwota, 0.0001);
        }

        [TestMethod]
        public void Podsumowania_LiczaSztukiISumePrawidlowo()
        {
            var m = _logika.Produkty.First(p => p.Nazwa == "Margherita");
            var pe = _logika.Produkty.First(p => p.Nazwa == "Pepperoni");

            m.IsSelected = true; _logika.DodajDoKoszyka(); // M x1
            m.IsSelected = true; _logika.DodajDoKoszyka(); // M x2
            pe.IsSelected = true; _logika.DodajDoKoszyka(); // P x1

            Assert.AreEqual(3, _logika.RazemSztuk);
            Assert.AreEqual(24 * 2 + 30, _logika.RazemKwota, 0.0001);
        }

        [TestMethod]
        public void ZapiszParagon_TworzyPlikJson()
        {
            _logika.Produkty[0].IsSelected = true;
            _logika.DodajDoKoszyka();

            // pracujemy w katalogu tymczasowym, żeby nie śmiecić w repo
            var tmp = Path.Combine(Path.GetTempPath(), "PizzeriaTest_" + Path.GetRandomFileName());
            Directory.CreateDirectory(tmp);
            var old = Directory.GetCurrentDirectory();
            Directory.SetCurrentDirectory(tmp);

            try
            {
                _logika.ZapiszParagon();
                var plik = Path.Combine(tmp, "paragon.json");

                Assert.IsTrue(File.Exists(plik), "Plik paragon.json powinien zostać utworzony.");
                var content = File.ReadAllText(plik);
                Assert.IsTrue(content.Contains("Margherita"));
            }
            finally
            {
                Directory.SetCurrentDirectory(old);
                try { Directory.Delete(tmp, true); } catch { }
            }
        }
    }
}

Test z zapisem JSON zmienia katalog roboczy na tymczasowy, żeby nie zostawiać plików.


4) Uruchom testy

  • Otwórz Test → Test Explorer
  • Kliknij Run All Tests
  • Wszystkie powinny świecić się na zielono. Jeśli któryś jest czerwony — kliknij i przeczytaj szczegóły błędu.

5) (Opcjonalnie) Drobne ulepszenia pod testy

To możesz zrobić później — testy i tak już działają:

  • Zmień kwoty na decimal (zamiast double) — precyzja pieniędzy.
  • Dodaj przeciążenie: public void ZapiszParagon(string sciezka) { ... } W testach zapisujesz wtedy wprost do Path.GetTempFileName() bez kombinowania z CurrentDirectory.

6) Najczęstsze problemy i szybkie fixy

  • Brak referencji do projektu Pizzeria: testy „nie widzą” klas → dodaj Project Reference (krok 2).
  • Inne przestrzenie nazw: dopasuj using Pizzeria.Logika; / using Pizzeria.Modele;.
  • Różnice w nazwach właściwości: jeśli zmienisz coś w modelu (np. CenaCenaJednostkowa), popraw asercje.
  • Błąd przy zapisie: brak uprawnień/katalogu → używaj katalogu tymczasowego jak w teście ZapiszParagon_TworzyPlikJson.

Co powinieneś zobaczyć na koniec

Struktura rozwiązania:

Solution
├─ Pizzeria
│  ├─ Logika/LogikaAplikacji.cs
│  ├─ Modele/ModelProdukty.cs, ModelKoszyk.cs
│  └─ ...
└─ Pizzeria.Tests
   ├─ LogikaAplikacjiTests.cs   ← tu są testy
   └─ Pizzeria.Tests.csproj

Test Explorer: 6 zielonych testów (lub więcej, jeśli dodasz swoje).