Spring context w zastosowaniu testów (Mockito)

Spring context w zastosowaniu testów (Mockito)
EF
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:53
0

Cześć,
Chciałem wejść w temat testów ogólnie w Springu, a szczególnie w temat Mockito.
Aby przejść do testowania należy mieć określony plik .xml z contextem aplikacji. W mojej strukturze mam plik* /WEB-INF/dispatcher-servlet.xml*, który zawiera same informacje i beany konfiguracyjne, np. handlerMapping, entityManagerFactory itd. Mam też plik resources/spring/application-config.xml, który został mi automatycznie dorzucony przez STS i nie zawiera w sumie nic, poza suchą definicją XMLa samego w sobie. Korzystając z wątku: http://stackoverflow.com/questions/16458754/spring-web-mvc-dispatcher-servlet-xml-vs-applicationcontext-xml-plus-shared dowiedziałem się, że taki właśnie podział na oba pliki powinien może istnieć. Wiadomo, to tylko nazwy, ale chodzi bardziej o logiczne rozdzielenie zawartości obu plików.

Przechodząc do właściwego pytania - moja klasa testująca:

Kopiuj
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/application-config.xml")
public class GetBookTest {
	@Autowired
	private BookService bookService;
	@Test
	public void getSingleBookShouldReturnBook() {
	    Object result = bookService.findById(1);
...

Pierwszym problemem przy najprostszym wywołaniu service jest NullPointer na linijce: Object result = bookService.findById(1);chociaz obiekt o takim id istnieje, zresztą próbowałem na różnych. Czy może być to związane z niepoprawnym określeniem kontekstu? Nie ma tam żadnych beanów, bo nie miałem potrzeby tworzenia takowych w application-context.xml. Też ciężko jest wskazać na lokalizację dispatcher-servlet.xml, której nijak nie chce Spring załapać. W takim układzie czy przenieść gdzieś indziej dispatcher-servlet,xml, czy zmodyfikować w jakiś sposób application-context.xml?

Aha i zapomniałem o jednym fakcie: NullPointer dostaję bez @Autowired, po dodaniu ten adnotacji krzyczy:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'biblioteka.GetBookTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.test.library.service.BookService biblioteka.GetBookTest.bookService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.test.library.service.BookService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Po dodaniu do application-context.xml jedynej deklaracji, która tam teraz się znajduje:

Kopiuj
<context:component-scan base-package="pl.test.library"/> 

Dostaję komunikat Failed to load ApplicationContext.

edytowany 5x, ostatnio: efem
M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
4

Pomieszałeś temat testów jednostkowych (Mockito) oraz testów integracyjnych (Spring Testing).

Jeżeli chcesz testować jednostkowo nie potrzebujesz ładować testów Spring, wystarczy samo Mockito. I mockowanie zależności (DI).

Jeżeli chcesz testować integracyjnie (Spring Testing) to nie chcesz używać mockito, a pracować z prawdziwymi obiektami.

Właściwie to co chcesz zrobić?

edytowany 2x, ostatnio: margor90
EF
Testy jednostkowe
M9
W takim razie proponuje zapomnieć o ładowaniu kontekstu Spring. Ponieważ korzystasz z Field Injection potrzebujesz adnotacji: @InjectMocks, @mock, @MockitoJUnitRunner.
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1

jw. jeśli to maja być unit testy to nie potrzebujesz ladować żadnych kontekstów. Np. Dla EasyMocka (którego osobiście polecam :P) wyglądałoby to tak:

Kopiuj
@Named
class Something{
    @Inject
    private Service service;
 
    public int method(){
        return service.f();
    }
}

I test:

Kopiuj
@RunWith(EasyMockRunner.class)
public class SomethingTest {
    @TestSubject
    private final Something something = new Something();

    @Mock
    private Service service;

    @Test
    public void testMethod(){
        int expectedResult = 1;
        expect(service.f()).andReturn(expectedResult).once();
        replay(service);

        int result = something.method();

        verify(service);
        assertEquals(expectedResult, result);
    }

Można też użyć innego runnera (np. jeśli używamy powermocka i potrzebujemy startować z PowerMockRunner.class) ale wtedy trzeba dorzucić

Kopiuj
    @Before
    public void setUp() {
        EasyMockSupport.injectMocks(this);
    }

"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
0

Czyli jestes fanem field injection?

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
3

Tak bo to jedyne rozwiązanie które realnie wspiera enkapsulacje i nie wymusza zaśmiecania kodu szczegółami które nie są w żaden sposób istotne. Milion setterów albo konstruktory na tysiąc argumentów wyglądają brzydko i zwyczajnie zaciemniają kod.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
M9
Constructor injection często wymusza redundantny, bezargumentowy i bezsensowny konstuktor (np. jak się pracuje z EJB i JPA).
0

Setery to sie zgadzam, ale jesli masz w klasie tyle zaleznosci, ze masz milion parametrow to cos jest nie tak. Ale o tym juz wielu wiele napisalo. Dobrze wiedziec jak w CERN ucza pisac.

Co do zasmiecania kodu masz racje, polecam przyjrzec sie Lombokowi. Mozesz napisac np. tylko pola prywatne finalne, a konstruktor dla ciebie bedzie automatycznie wygenerowany.

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

Ja oczywiście trochę przesadzam z tym milionem parametrów, niemniej uważam że w klasie serwisowej, której sam i tak nie tworze, posiadanie konstruktora jest zwyczajnie bez sensu i zaśmieca kod tak samo jak settery. Po co mi kod którego nigdy nie używam? Im mniej kodu tym lepiej.
Poza tym może i mam trochę taką manierę żeby stosować SRP gdzie sie da i potem moje klasy korzystają z wielu małych, specjalizowanych, łatwo testowalnych klas, co niestety przekłada się jednocześnie na to że mają dodatkowe zależności. Wolę wstrzyknąć sobie 10 mini klas na 40 linijek każda niż zrobić jednego wielkiego klocka na 1000 linii ;]

A co do tego jak "uczą" w CERNie pisać to w ramach ciekawostki na przykład:
https://twiki.cern.ch/twiki/bin/view/CMSPublic/SWGuidePythonTips#Running_on_more_than_255_files
czyli jak ominąć "problem" limitu argumentów funkcji w pythonie który wynosi 256 :D Fizycy powinni mieć jakiś zakaz programowania ;]

edit: Robimy tu mega offtop. Ja chętnie na ten temat podyskutuje, więc jeśli cię to jakoś bardziej interesuje to zrób o tym nowym wątek gdzieś w Inżynierii Oprogramowania ;)


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 3x, ostatnio: Shalom
0

@margor90 - mozesz wyjasnic? Jak masz bezargumentowy konstruktor, to gdzie tam constructor injection?

Co do field injection - ja mam z tym taki problem, ze jesli sie nie uzywa kontenera DI to nie da sie obiektu takiej klasy stworzyc zwyklym new. Nie mozna miec pol final, a ja bardzo lubie final.

@Shalom - a to trzeba podawac to jako pojedyncze argumenty? Nie mozna jednej listy czy jakiegos tam iterable przekazac? Do glowy by mi nie przyszlo cos takiego, no ale ja glupi jestem.

H1
  • Rejestracja:około 10 lat
  • Ostatnio:ponad 6 lat
  • Postów:185
1

@margor90 dobrze prawi.

Przy Mockito miałbyś coś podobnego:

Kopiuj
Mockito.when(bookService.findById(Mockito.eq(1))).thenReturn(expectedResult);

Mockito możesz dać jako static import.

Do asercji polecam AssertJ -> assertThat(actual).isEqualTo(something) itp.

edytowany 4x, ostatnio: H1ghlander
M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
0

@margor90 - mozesz wyjasnic? Jak masz bezargumentowy konstruktor, to gdzie tam constructor injection?

Chodziło mi, że coś takiego się nie zadeploy'uje:

Kopiuj
@Stateless
@LocalBean
class MyEjbService {
  
    private Srv1 srv1;
    private Srv2 srv2;

    @Inject
    public MyEjbService(Srv1 srv1, Srv2, srv2) {
    }
    
    // business logic

}

Takie coś już tak:

Kopiuj
@Stateless
@LocalBean
class MyEjbService {
  
    private Srv1 srv1;
    private Srv2 srv2;
 
    public MyEjbService() {
    }

    @Inject
    public MyEjbService(Srv1 srv1, Srv2, srv2) {
    }
    
    // business logic

}

Takie coś też, bo konstuktor jest trywialny i wygeneruje się sam (dlatego wolę field injection, wedle zasady YAGNI):

Kopiuj
@Stateless
@LocalBean
class MyEjbService {
  
    @Inject
    private Srv1 srv1;
    @Inject
    private Srv2 srv2;
    
    // business logic

}
0

Chcesz mi powiedziec, ze EJB 3.x nie wspieraja constructor injection bez dodatkowego no-arga? Pierwsze slysze, ale nie pisalem w Java EE juz od dawna.

H1ghlander napisał(a):

Przy Mockito miałbyś coś podobnego:

Kopiuj
Mockito.when(bookService.findById(Mockito.eq(1))).thenReturn(expectedResult);

Mockito możesz dać jako static import.

Do asercji polecam AssertJ -> assertThat(actual).isEqualTo(something) itp.

WAT? A co to ma wstrzykiwania?

H1
o Mockito też pytał :P
M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
0
ucilala napisał(a):

Chcesz mi powiedziec, ze EJB 3.x nie wspieraja constructor injection bez dodatkowego no-arga? Pierwsze slysze, ale nie pisalem w Java EE juz od dawna.

http://stackoverflow.com/questions/9173592/can-i-use-cdi-constructor-injection-for-ejbs

Zdaje się, że specyfikacja tego wymaga (The EJB 3.1 spec, section 4.9.2 Bean Classes). Jak wiadomo specyfikacja, a implementacja to dwie różne rzeczy, więc co serwer aplikacyjny może być inaczej. Więc bezpiecznie jest dodawać bezargumentowy konstruktor (kiedyś mi się nie deploy'owalo) lub robić field injection.

Uznałem, że jak używam field injection to nie ma żadnego problemu, zwłaszcza że Mockito bez problemu nagina rzeczywistość na potrzeby testów (refleksja).

edytowany 1x, ostatnio: margor90
Shalom
Powermock to dopiero nagina rzeczywistość :P mock static, mock final, suppress static initialization, mock new ;)
0

To jest dopiero WTF. Nie dziwie sie ze jak sie pisze w EE to pozniej sie wymysla dziwne rzeczy ;d
No, ale na powaznie, nie spodziewalem sie ze jest az tylu (wszyscy tutaj poza mna, wykazane empirycznie poki co) proponentow field injection. Ale kazdy robi to co lubi. Peace.

0

Ciekawe, w watku SO ktory podlinkowales wypowiada sie nie kto inny jak Pete Muir of CDI fame i twierdzi, ze to jest bug jesli CDI jest rowniez 'wlaczone' dla takich EJB (zakladam ze chodzi o to, za jar z tymi EJB ma rowniez META-INF/beans.xml czy jakos tak to bylo).

M9
Pewnie zależy do od konfiguracji w beans.xml. Jeśli każdy bean (również EJB) jest beanem CDI to wtedy zasady CDI zostaną zastosowane do EJB (bean-discovery-mode="all"). http://www.adam-bien.com/roller/abien/entry/when_your_di_breaks_bean. Domyślnie jest 'annotated'. Ale nie sprawdzałem. Tyle, że jak coś jest beanem EJB to i tak powinno IMO być obsługiwane zgodnie ze specyfiacją EJB, nawet jeśli jednocześnie jest beanem zarządzanym przez EJB i CDI. A skoro w specyfikacj wymagają tego konstruktora to IMO niech już będzie, nie jest to taki wielki wymóg dla field DI.
M9
Poza tym kiedy CDI jest włączone? Kiedy jest annotated lub all. Czyli prawie zawsze.
0

@margor90 - no Ok, ale sam sobie przeczysz. Napisales ze c-tor injection wymaga dodatkowych beanow, ale to nie wina c-tor injection tylko durnych specyfikacji. W watku ktory linkowales Pete powiedzial ze JBoss (Wildfly?) nie wymaga tego, i pewnie nie ma technicznych wymagan, tylko jakies tam przestarzale zdanie z wpecyfikacji sprzed CDI, ktorego zapomnieli skasowac.
Tylko mnie utwierdza w przekonaniu ze Spring to jednak lepszy wybor ;d

M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
0

Spring tez nie jest doskonaly. Nie obsluguje np. pol obiektow dla bezstanowych singletonow w prosty sposob i ludzie robia cuda na kiju ze scope prototype, aby ograniczyc np. liczbe watkow jaka moze pracowac jednoczesnie na bezstanowym singletonem. Dla malych projektow nie ma to znaczenia.

Poza tym WildFly to jest dosc dziwny serwer. Domyslnie jest skonfigurowany tak, ze nie ma tam pol obiektow tylko bezstanowe singletony (jak w Springu).

Dla mnie osobiscie nie ma wiekszego znaczenia czy pracuje ze Springiem czy JEE. Doceniam Springa za mnogosc projektow i bajerow jakich nie ma w JEE. I przede wszystkim za Spring Testing. Jednak aby zrobic cos na szybko to JEE7 nadaje sie swietnie (ale to nie dzial flame).

0

Co to jest 'pol obiektow'? Masz na myslic object pool, czyli pule?
Po co pule bezstanowych singletonow? Jelsi masz ich kilka, to co to za singleton? Jesli cos jest bezstanowe, to po co robic pule, skoro jeden obiekt wystarczy bez wzgledu na to ile jest watkow - w koncu jest bezstanowy?

M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
0

Co to jest 'pol obiektow'? Masz na myslic object pool, czyli pule?

Tak.

Jesli cos jest bezstanowe, to po co robic pule, skoro jeden obiekt wystarczy bez wzgledu na to ile jest watkow - w koncu jest bezstanowy?

Serwery maja skonczona liczbe zasobow. Kazdy nowy watek to jest jakies obciazenie. Pula pozwala ograniczyc ilosc klinetow jaka korzysta z danej uslugi (service): bo np. jednoczesnie usluge moze swiadczyc tylko 70 obiektow bezstanowych. Jak brakuje zasobow, klient czeka. W przypadku, gdy zasob jest nie tak duzo (bo np. obciazenie jest ogromne) dzieki puli klient poczeka az bedzie wolny bezstanowy obiekt.

W przypadku singletona zacznie dzialac i moze byc trudniej ograniczac zuzycie zasobow (niespodziwany przyrost obciazenia jak niebezpiecznie wzrosnie liczba klientow).

Zgadzam sie, ze generalnie dla malo / zwyczajnie obciazonych serwerow pula @stateless / @Singleton nie ma znaczenia. Tam gdzie sa problemy ze skalowalnoscia to moze miec znaczenie. Tak samo jak liczba jednoczesnie uruchamianych asynchronicznych wywolan (nowe watki).

edytowany 3x, ostatnio: margor90
NoZi
  • Rejestracja:około 16 lat
  • Ostatnio:około 5 godzin
0

Polecam Spocka zamiast mockito,junit,assertj


Hate the sin, love the sinner
0

Nie rozumiem co chcesz powiedziec. Jelsi server moze ograniczac liczbe watkow dla 70 obiektow, to dla 1 nie moze? Nie moze ustalic ze 'powiadam, ten jeden jedyny singleton prawdziwy bezstanowy moze byc uzywany przez ile watkow sie da, ale tych watkow nie utworze wiecej niz 70'?
Czy twierdzisz, ze majac 70 obiektow bezstanowych serwer nie musi tworzyc wielu watkow, bo ma juz przeciez 70 obiektow? He? Cos krecisz.

0
NoZi napisał(a):

Polecam Spocka zamiast mockito,junit,assertj

Podpisuje sie pod tym, dodajac iz Spring Test wspiera Spocka!

M9
  • Rejestracja:prawie 10 lat
  • Ostatnio:prawie 6 lat
0

Nie jestem autorytetem w kwestii EJB i ani Springa (ucze sie jednego i drugiego). Ale moge podeslac ciekawy artykul:
http://www.adam-bien.com/roller/abien/entry/do_we_need_stateless_session

Istotny cytat:

The only thing to remember is: every transaction runs in a dedicated Session Bean instance and thread.

Konkludujac, przekombinowuje, tu chodzi o wygode.

So pooling is not needed for performance or scalability reasons, rather than for the ease of use. You can easily restrict the scalability and you get a single threaded programming model for free. In day to day development you don't have to thing about pooling or instance management.

http://www.adam-bien.com/roller/abien/entry/is_ejb_3_the_solution

W Springu na pewno tez mozna to skonfigurowac, czy latwiej watpie.

edytowany 2x, ostatnio: margor90
H1
  • Rejestracja:około 10 lat
  • Ostatnio:ponad 6 lat
  • Postów:185
0

Chciałbym zapytać o Spocka... który naprawdę wygląda ślicznie.

Spock to jest niby BDD. Czy to prawda, że BDD is TDD done right?

tu jest fajny tutek http://thejavatar.com/testing-with-spock/

edytowany 1x, ostatnio: H1ghlander
M9
IMO BDD to jest buzzword
EF
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:53
0

Ok, temat trochę uciekł w bok a nie chcę zakładać nowego, bo w sumie moje kolejne pytanie mieści się w obrębie oryginalnego pytania, otóż - mam kontroler:

Kopiuj
@RequestMapping(value = "/bookDetails/{bookId}")
	public String detBook(@PathVariable("bookId") int bookId, @ModelAttribute("book") ShowBookCommand command) {

		command.setBookDto(BookMapper.map(bookService.findById(bookId)));
		command.setBorrowers(BorrowerMapper.map(borrowerService.findAll()));

		return "bookDetails";
	}

ShowBookCommand jest klasą, która przechowuje zarówno obiekt książki (BookDto) jak i listę wypożyczających.
Chcąc napisać pod to test jednostkowy z użyciem MockMVC w podany sposób:

Kopiuj
@Mock
	private BookService bookService;

	@Mock
	private BorrowerService borrowerService;

	@Mock
	View mockView;
	
	@InjectMocks
	private DetailsBookController detailsBookController;

	private MockMvc mockMvc;

	Borrower borrower;
	
	@BeforeTest
	public void setup() {

		MockitoAnnotations.initMocks(this);
		this.mockMvc = MockMvcBuilders.standaloneSetup(detailsBookController).setSingleView(mockView).build();
	}

	@Test
	public void testCreateBook() throws Exception {
		List<Borrower> borrowerList = Arrays.asList(borrower, borrower, borrower);
		when(bookService.findById(999)).thenReturn(new Book());
		when(borrowerService.findAll()).thenReturn(borrowerList);
		 
		 this.mockMvc.perform(post("/bookDetails/{bookId}", "999"))
         .andExpect(status().isNotFound())
         .andExpect(view().name("bookDetails"));
	}

Dostaję komunikat:

java.lang.AssertionError: No ModelAndView found

Pisząc ten test wzorowałem się dwóch tutkach:
http://www.luckyryan.com/2013/08/24/unit-test-controllers-spring-mvc-test/
http://myshittycode.com/2014/01/16/mockmvc-mockito-epic-tests/

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0
  1. Dlaczego oczekujesz statusu isNotFound?
  2. Wysyłanie danych do widoku poprzez przyjmowanie jako argumentu metody kontrolera @ModelAttribute to terroryzm. To służy do ODBIERANIA danych z formularza na stronie, a nie do wysyłania danych do widoku. Faktem jest że można to "przy okazji" w ten sposób wykorzystać, ale nie znaczy że to dobra praktyka. Po to jest klasa ModelAndView którą generalnie sie zwraca z nie-restowego kontrolera, zamiast jakiegoś stringa...

"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
EF
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:53
0
  1. Fakt, poprawiłem.
    Zauważyłem też, że miałem zły adres (bez /action na który zapięta jest cała klasa). Aktualny kod:
Kopiuj
this.mockMvc.perform(post("/action/bookDetails/{bookId}", "999"))
         .andExpect(status().isOk())
         .andExpect(view().name("bookDetails"));

Wyrzuca:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException

na linijkę:

this.mockMvc.perform(post("/action/bookDetails/{bookId}", "999"))

  1. Takie wymaganie, że musi zwracać Stringa. Wcześniej miałem zrobione na ModelAndView właśnie. Niemniej we wklejonych przeze mnie tutkach też jest String
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
1

No i czego oczekujesz? Zapnij się debugerem i zobacz co jest nullem.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
EF
Nie zainicjowana zmienna, no comment
EF
  • Rejestracja:ponad 9 lat
  • Ostatnio:ponad 7 lat
  • Postów:53
0

Chciałem uzyskać nieprawidłową walidację przy JSR 303. W klasie testującej daje:

Kopiuj
@Mock
	private Validator validator;
...
doAnswer(new Answer<Object>() {
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                Errors errors = (Errors) invocationOnMock.getArguments()[1];
                errors.reject("forcing some error");
                return null;
            }
        }).when(validator).validate(anyObject(), any(Errors.class));

Na kontrolerze:
... result.hasErrors()...
Problem jest taki, że nie generuje tego błędu. Zaznaczam, że nie mam własnego validatora tylko korzystam z domyślnego. Ktoś kojarzy jak zmusić to przesłania błędu walidacji?

edytowany 2x, ostatnio: efem
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)