Spring Boot Test problem z konfiguracją Contextu

Spring Boot Test problem z konfiguracją Contextu
PI
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 22 godziny
  • Postów:35
0

Cześć,

mam nietypowy problem.
Mam testy w SpringBoocie. Jak odpalam tylko tą jedną klasę to wszystko działa.
Natomiast, jak odpalę wszystkie na raz, to ta klasa failuje.
Są to testy integracyjne - test opiera się w głównej mierze do zapisu do bazy i odczytu z niej rekordu.
W konsoli widzę bezpośrednio insert, z poziomu kodu nawet specjalnie wypisuje sobie jej ID, ale select nie zwraca nic. I mam mocne WTF
Select robię za pomocą JPA Repository:

Kopiuj
@Autowired
KlasaRepository klasaRepository;

W klasie testowej

Spotkał się ktoś z czymś takim, nie do końca wiem, gdzie i co szukać.

Klasa wpięta z:

Kopiuj
@AutoConfigureTestDtabase
@SpringBootTest
@DirtiesContext
@ExtendWith(SpringExtension.class)

Za nakierowanie, pomoc z góry dzięki

edytowany 2x, ostatnio: pitagram
PI
  • Rejestracja:ponad 9 lat
  • Ostatnio:3 miesiące
  • Postów:2787
0

klasa failuje

jaki dokładnie błąd leci?

I odpalasz pojedynczy i wszystkie task samo (czyli np oba poprzez IJ lub oba poprzez terminal)?

PI
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 22 godziny
  • Postów:35
0

@Pinek

Znaczy błąd leci taki, że w tej klasie, test opiera się na wywoływaniu metody, która zapisuje do bazy danych.
Natomiast po tym jest wykonywana metoda z repozytorium.
findByID

I tutaj jest fail, ponieważ nie znajduje tego rekordu. Choć tak jak wspomniałem w konsoli widzę, że jest zapis, wypisuje sobie specjalnie nawet ID.
Co jest najdziwniejsze jak odpalę tylko tą klasę to działa, ale jak odpalam wszystkie klasy testowe, to już nie działa. Nie znajduje wspomnianego ID

Za każdym razem odpalam z poziomu IDE z takimi samymi parametrami.

Skoq
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Kraków
  • Postów:255
0

Pokaż ten test


I tak to właśnie jest
PI
jasne, za chwilę postaram się wrzucić. Niestety nie mogę wrzucić całego projektu.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4706
1

jak odpalam wszystkie klasy testowe, to już nie działa. - rozumiem, że odpalasz z poziomu IDE wszystkie testy projektu. Sekwencyjnie.

A czy jak wywołujesz findByID to ta transakcja ma prawo widzieć zmiany? (Może jest otwarta wcześniej?).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
Zobacz pozostały 1 komentarz
jarekr000000
No to wydaje Ci się. Pytanie numer jeden to jak wyglądają tam transakcje i izolacja transakcji. Bo może masz Serializable i zmian nie widać. Choć jest to dość nietypowe.
PI
@jarekr000000: transakcja jest tworzona dopiero na wywoływaniu metody saveAndFlush Nie ma bezpośredniej izolacji. Nie do końca rozumiem przekazu z Serializable, ale nie implementowałem go nigdzie w tej częsci aplikacji. Zgadza się, że nietypowe, bo nie jestem w stanie zrozumieć dlaczego działa, jak odpalam jedną klasę to działa, ale jak wszystkie to już nie.
jarekr000000
No ale findById też może być w jakiejś tranzakcji...
jarekr000000
Poza tym saveAndFlush nie gwarantuje przecież, że dane są zacommitowane. Strasznie długo już dyskutujemy, a Ty nie pokazałeś kodu. Trochę to bez sensu. To jest Spring - tu każde drobne g**no może wszystko rozwalić.
PI
@jarekr000000: wrzuciłem kod. Nawet jeśli coś jest w jakiejś transakcji, to nie powinno mi wywalić wtedy testu nawet gdy go odpalam tylko ten jeden
S9
  • Rejestracja:około 4 lata
  • Ostatnio:około 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
1

A daj @Transactional na klasę testowa, powinno zadziałać


edytowany 1x, ostatnio: scibi_92
Zobacz pozostały 1 komentarz
PI
Niestety nie pomogło
TY
Najwyższy czas na najlepszą adnotację na świecie: @EnableEverything
jarekr000000
@Tyvrel: pomysł bardzo dobry. Jak znam życie to na pewno już kilka razy próbowany (można sobie zdefioniować samemu taką adnotację). Osobiście widze jeszcze miejsce na adnotację @Please. Rodem z INTERCALL, trzeba by co jakiś czas wstawiać, bo jak nie to spring się obrazi. Ale za często, bo może stwierdzić, że za bardzo włazisz w d...
I1
Powinna być adnotacja @ReleaseTheKraken :)
jarekr000000
@i1010011010: taka już jest - tylko dla niepoznaki nazywa się @SpringBootApplication
PI
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 22 godziny
  • Postów:35
0

Poniżej przesyłam kod, jest on obkrojony, ale generalnie nie różni się.(Niestety nie mogę tu wrzucić całego projektu, choć tak by było prościej)

Kopiuj
@Entity
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SimpleEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Long longColumn;
    private String firstColumn;
}
Kopiuj
@Data
@AllArgsConstructor
public class SimpleEntityDto {

    private Long longColumn;
    private String firstColumn;
}
Kopiuj
@Repository
public interface SimpleEntityRepository extends JpaRepository<SimpleEntity, Long> {
}
Kopiuj
@Service
@RequiredArgsConstructor
public class SimpleEntityService {

    private final SimpleEntityRepository simpleEntityRepository;

    public void saveSimpleEntity(SimpleEntityDto simpleEntityDto) {

        SimpleEntity simpleEntity = SimpleEntity.builder()
                .firstColumn(simpleEntityDto.getFirstColumn())
                .longColumn(simpleEntityDto.getLongColumn())
                .build();

        SimpleEntity savedSimpleEntity = simpleEntityRepository.saveAndFlush(simpleEntity);
    }
}
Kopiuj
@ExtendWith(SpringExtension.class)
@AutoConfigureTestDatabase
@SpringBootTest
@Transactional
public class SimpleEntityServiceTest {

    @Autowired
    SimpleEntityService simpleEntityService;

    @Autowired
    SimpleEntityRepository simpleEntityRepository;

    @Test
    public void shouldSaveSimpleEntity() throws Exception {

        SimpleEntityDto simpleEntityDto = new SimpleEntityDto(99L, "string column");

        simpleEntityService.saveSimpleEntity(simpleEntityDto);

        SimpleEntity simpleEntity = simpleEntityRepository.findById(1L).orElseThrow(() -> new Exception("Did not find Simple Entity"));

        assertEquals(1L, simpleEntity.getId());
        assertEquals(99L, simpleEntity.getLongColumn());
        assertEquals("string column", simpleEntity.getFirstColumn());

    }
}

Mam nadzieje, że coś pomoże, choć pewnie będzie ciężko, bądź niemożliwe mając na uwadze to, że Test failuje tylko, gdy się uruchomi wszystkie raz
Bo tak jak wspomniałem zawilej logiki w nim nie ma.

jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4706
3

simpleEntityRepository.findById(1L).

A skąd wiesz, że 1 ???


jeden i pół terabajta powinno wystarczyć każdemu
PI
Z racji, tego, że baza jest uruchamiania w pamięci, startuje nowa instancja i numeracja zaczyna się od 1 Ale też pomyślałem nad tym. Dlatego tak jak wspomniałem przypisuję Encje do Obiektu i następnie z niej wypisuje ID. Choć do bazy leci Insert to Select już nie nie zwraca. Dodam, że jak sprawdzałem za pomocą findAll to również nic nie było zwracane.
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2363
1
Kopiuj
  SimpleEntityDto simpleEntityDto = new SimpleEntityDto(99L, "string column");

  simpleEntityService.saveSimpleEntity(simpleEntityDto);

  SimpleEntity simpleEntity = simpleEntityRepository.findById(1L).orElseThrow(() -> new Exception("Did not find Simple Entity"));

  assertEquals(1L, simpleEntity.getId());
  assertEquals(99L, simpleEntity.getLongColumn());

A skąd ta jedynka ma się wziąć w bazie? To co zapisujesz to ID=99L. , więc powinno się wywalić przy wyszukiwaniu na 1 i później na asercji. Chyba, ze coś mi umknęło :)

PI
99 to jest zwykła kolumna, Która widnieje w zwykłym DTO. Zrobiłem to w ten sposób, żeby maksymalnie odzwierciedlić rzeczywistą aplikację ID jest auto inkrementowane na poziomie bazy
Skoq
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Kraków
  • Postów:255
1

Postaw breakpointa po zapisie > evaluate expression i pobierz wszystko z bazy, zobacz czy cokolwiek tam jest. Stawiasz jakiś kontener do testów czy inmemory db?


I tak to właśnie jest
PI
Na poziomie kodu, jak odpalę findAll to mam zwracane zapisany obiekt. Na poziomie testów, gdy wywołam findAll na repo to nie dostaje nic. Jest to baza InMemory
KA
  • Rejestracja:ponad 5 lat
  • Ostatnio:około 6 godzin
  • Postów:57
1

Nie podoba mi się pomysł @Transactional nad klasą z testami, bo już nie raz widiziałem przypadki że encja w testach nie dotykała nawet bazy, bo kolejny find z repo korzystał dalej z cache JPA:

Kopiuj
@Transactional
public class SimpleEntityServiceTest {
edytowany 1x, ostatnio: Karaczan
PI
@Karaczan: Przepraszam, zostało po testowałem pomysł od @scibi_92<br /> Jednak efekt finalny jest taki sam, z adnotacją lub bez.
KA
a racja, nie zauważyłem tamtego posta :)
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4706
2

Nie mam dalszych pomysłów.
IMO magia springa - jak zwykle - jednak nie działa. Czyli, popełniłeś gdzieś drobny błąd i teraz jego szukanie to kwestia szczęścia (i doświadczenia)
Masz dwa wyjścia:

  1. Najfajniej jakby udało Ci się wykroić minimalny "powtarzalny" przykład i wrzucić na GH - chyba nadal lubie ( i nie tylko ja) takie gówna śledzić.
  2. Jak to w pracy - to możesz zrobić Na niemieckiego architekta: oznacz test jako @Ignore i udawaj, że to nie twój problem. Wyjebie się na produkcji, ale to spring, więc i tak by się wyjebało :-)

jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
PI
@jarekr000000: jasne, jeśli będzie to możliwe to jak najbardziej się postaram tak zrobić i wrzucić próbkę. Ja już niestety wystrzelałem się z pomysłów i nie wiem kiedy mnie olsńi z rozwiązaniem problemu.
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2363
2

Możliwe, że dla tej bazy in-memory strategia strategy = GenerationType.IDENTITY działa w oparciu o sekwencję, jeśli więc jedziesz testami sekwencyjnie oraz:
Test#1 - robi create SimpleEntity -> ID=1 zostaje zjedzone, sekwencja bazodanowa zostaje podbita do 2, test czyści dane z SimpleEntity (ale sekwencja nadal żyje)
Test#2 - robi createSimpleEntity -> ID z sekwencji 2 -> sekwencja podbita do 3

Możliwe, że gdzieś w testach potrzebujesz jawnego restartu sekwencji (@Before/@After)
ALTER SEQUENCE DUPA RESTART WITH 1;

Przy testach uruchamianych sekwencyjnie, nie wyjaśnia to jednak braku wpisów dla findAll. W testach uruchamianych równolegle byłaby szansa na race condition.

Jaka to konkretnie baza in-memory? Czy masz pulę wątków połączeń?

Inne wyjaśnienie dla tego findAlla, to właśnie pula połączeń i np. wstawiasz rekordy w oparciu o connection#1, findAlla robisz na connection#2 (wówczas zmiana rozmiaru puli połączeń bazodanowych na 1 byłaby pewnego rodzaju obejściem problemu).

Debugger prawdę Ci powie ;-)

jarekr000000
Debugger prawdę Ci powie otóż dość często nie
PI
@yarel: Jest to H2, wpięta i skonfigurowana automatycznie poprzez adnotację @AutoConfigureTestDatabase Nie konfigurowałem pulę wątków połączeń. Spróbowałem teraz to ustawić poprzez properties: spring.datasource.max-active=1 ale niestety bez efektu. Szkoda, bo wydawało mi się to dobrą wskazówką. Chyba, że to niepoprawnie ustawiam?
Skoq
Najlepiej jakbyś h2 wywalił i użył testcontainers do postawienia bazy dla testów
YA
@jarekr000000: masz jakąś historię z życia o debuggerach, które prawdy nie mówią?
jarekr000000
@yarel: nic specjalnego, ale to, że deguggery kłamią to norma - same rozwiązują się problemy z wielowątkowością, inicjalizacją kolekcji hibernate itp.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4706
1

Skoro H2 ... to H2 ma taki jeden śmieszny ficzur, który czasem potrafi doprowadzić do łez.
Poczytaj o ;DB_CLOSE_DELAY=-1

Nie wiem czy to jest problemem, ale jest jakaś szansa.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 1x, ostatnio: jarekr000000
RA
  • Rejestracja:około 3 lata
  • Ostatnio:dzień
  • Postów:54
0

Luźno przyglądam się tematowi i nie widząc całości kodu nie chcę zgadywać co może być nie tak. Natomiast IntelliJ ma taką fajną opcję, jak puszczanie testów nonstop aż do faila. Bardzo często okazuje się to pomocne.

SA
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:94
0

Ja mam taki pomysł.
1.Skonfiguruj bazę H2 w pliku properties.
2.Niech metoda do zapisywania encji zwraca idka tej zapisanej encji.
3. W teście użyj tylko adnotacji @SpringBootTest (bazę po teście możesz wyczyścić jakimś after eachem z metodą deleteAll(), @Transactional lepiej nie używać jak nie ma takiej potrzeby.)
4.W teście wstrzyknij tylko serwis. Nie potrzebujesz tego repo, bo ten serwis zależy od niego. Musisz tylko dodać do serwisu metodę findById.
5.I teraz test będzie wygladał tak, że tworzysz sobie to dto, przekazujesz do metody save z serwisu, ta metoda zwraca idka zapisanej encji, potem w asercji za pomocą metody findById sprawdzasz czy istnieje taka encja i czy ma odpowiednie pola i już.

Charles_Ray
  • Rejestracja:prawie 17 lat
  • Ostatnio:około 20 godzin
  • Postów:1873
2

A jak w tym teście dasz na chama repo.findAll() i wyprintujesz wszystkie znalezione encje, to coś tam w ogóle jest czy nie? Czy baza jest pusta? Przy puszczeniu wszystkich testów oczywiście, bo ten przypadek debugujesz.

Podejrzewam, że tam się generuje jednak inny id niż 1 i jakieś rekordy jednak są.


”Engineering is easy. People are hard.” Bill Coughran
edytowany 2x, ostatnio: Charles_Ray
PI
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 22 godziny
  • Postów:35
0

Na wstępie Panowie bardzo dziękuje za aktywność i za chęci podjęcia pomocy. Poniżej wysyłam odpowiedzi + moje subiektywna opinia / to co udało mi się gdzieś tam przetestować

@Charles_Ray:
Właśnie przy wywołwaniu repo.findAll() nic nie zwraca, jest pusto.
Więc to nie jest kwestia wyszukiwania złego ID.

@jarekr000000: ;DB_CLOSE_DELAY=-1 .
Dodałem to, choć nie byłem pewien, czy mi to zadziała z adnotacją @AutoconfigureTestDatabase
Więc w properties ustawiłem bazę H2

Tylko teraz dostaję inny błąd przy odpaleniu wszystkich testów:
Unable to build HibernateSessionFactory; nested exception is org.hibernate.exception.GenericJDBCException: Unable to open JDBC Connection for DDL execution
Caused by: org.h2.jdbc.JdbcSQlException: Function "LOCK_MODE" not found; SQL statement:
CALL LOCK_MODE()

Z adnotacją @AutoconfigureTestDatabase ten problem się nie pojawia

Może to być jakiś trop. Rozumiem sam błąd, aplikacja normalnie korzysta z Oracle, ale tutaj jest wywoływana funkcja, która nie istnieje w H2.
Tylko w jaki sposób mógłbym ją znaleźć w kodzie. Wykorzystuje w projekcie IgniteCache i być może jakoś jego wewnętrzna funkcja to wykorzystuje.
Ale nie mam pomysłu na to

@yarel:
Poszedłem w kierunku puli wątków połączeń. Więc Usunąłem adnotację @AutoconfigureTestDatabase
Zestawiłem w properties dostęp do bazy H2
spring.datasource.hikari.maxium-pool-size=1 test już nie przechodzi
Dostaję błąd, że nie może zestawić połączenia.
HikariPool-1 - Connection is not available, request timed out after 30001ms.

Gdy ustawię
spring.datasource.hikari.maxium-pool-size=2
To już jest na zielono.

I test nie przechodzi nawet, gdy go odpalę pojedynczo. Nie jest w stanie nawiązać połączenia. Czy to jest normalne ? Może tu gdzieś widnieje przyczyna. Co sądzisz? Chyba, że tak sam w sobie Spring pod spodem działa.
Tak jakby były już tworzone dwa połączenia i wtedy by się zgadzało. Tylko jak to potwierdzić / naprawić

KA
  • Rejestracja:ponad 5 lat
  • Ostatnio:około 6 godzin
  • Postów:57
0

Może błąd jest w innym teście niż ten który pokazałeś? W sensie że ten co pokazałeś nie przechodzi, ale źródło problemu jest w innym miejscu.

edytowany 1x, ostatnio: Karaczan
PI
Ciężko powiedzieć, ale raczej wydaje mi się, że winna nie leży w teście w innym miejscu.
KA
a dużo masz tych testów? może warto wrzucić tutaj?
PI
niestety jest to kod biznesowy i nie jestem w stanie podrzucić. Gdyby tak nie było, to już dawno bym chciał to wrzucić, to by mocno ułatwiło weryfikację.
YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2363
0

Hmm, a jakbyś dorzucił ręcznie entitymanagera i dodał perista przed saveflush? Nie wiem na ile ta adnotacyjna magia przykrywa EMa...

PI
spróbuje się pobawić w wolnej chwili
PI
  • Rejestracja:ponad 6 lat
  • Ostatnio:około 22 godziny
  • Postów:35
1

Wołam, tych którzy się udzielali.
@Pinek , @Skoq , @jarekr000000 , @scibi_92, @yarel , @Karaczan , @raxigan , @Sampeteq , @Charles_Ray

Po części udało mi się rozwiązać, choć akurat jeszcze niestety nie udało mi się doszukać finalnej przyczyny, ponieważ nadal nie rozumiem w jaki sposób powstaje ten błąd.
Być może ktoś z wyżej wymienionych będzie w stanie wyjaśnić. Jak się sam czegoś dowiem, to napiszę.

Dziś akurat przysiadłem trochę nad tymi testami i zacząłem się bawić. Na wstępie zacząłem migrację do Junit5 , wyłączyłem wszystkie testy z Junit4
Co zmniejszyło ilość testów i pozwoliło mi zauważyć jedną rzecz.

Testy odpalane razem nie przechodzą, gdy są dwie klasy oznaczone adnotacją @AutoConfigureTestDatabase
W pliku properties mam zostawione tylko
spring.jpa.hibernate.ddl-auto=update

Klasy testowe są odpalone za kolejnością, więc po prostu dodałem to drugiej klasy
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)

I teraz już wszystko działa. Niestety jeszcze nie udało mi się ustalić dlaczego tak się dzieje, ale jak się dowiem to napiszę tutaj w temacie. Być może komuś się przyda ale już bez wywoływania.

Miłego Dnia :)

Charles_Ray
  • Rejestracja:prawie 17 lat
  • Ostatnio:około 20 godzin
  • Postów:1873
3

Jeżeli polegasz na kolejności odpalania testów, to dalej masz problem niestety. Ustal przyczynę.


”Engineering is easy. People are hard.” Bill Coughran
edytowany 2x, ostatnio: Charles_Ray
PI
Niestety wiem, będę starał się ustalić przyczyna. Tym bardziej, że nie lubię, gdy coś działa, ale nie wiem do końca jak / dlaczego.
Skoq
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Kraków
  • Postów:255
2

Ja bym na Twoim miejscu pozbył się tych adnotacji tj. @DirtiesContext i @AutoConfigureTestDatabase -> nigdy tego nie używałem ale widzę, że pierwsza niszczy kontekst, który musi być stawiany na nowo (po co?) a druga automagicznie coś konfiguruje pod spodem, lepiej imo ręcznie to zrobić w pliku konfiguracyjnym. W bazowej klasie, która jest rozszerzana przez resztę testów integracyjnych (zakładam, że taką masz) możesz wrzucić kawałek kodu, który będzie czyścił bazę po lub przed każdym testem coś w tym stylu:

Kopiuj
@Before
public void cleanup() {
  dupaRepository.deleteAll();
}

Będziesz miał pewność, że jeden test nie sypie się bo inny coś wstawił/zmienił. Masz prosty test, który nie ma prawa się wywalać a obstawiam, że to wina czegoś co robią te adnotacje xD


I tak to właśnie jest
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)