Mockito - test controllera - nieprawidłowa liczba elementów listy

0

Hej,

Uczę się robienia testów aplikacji z wykorzystaniem JUnit i Mockito. Próbuję zrobić test controllera przy pomocy mockMvc, ale nie mogę dojść do tego co jest nie tak.
Daje mocka na repozytorium, zeby zwracało listę z 1 elementem. Ale w teście nieważne co bym zrobił dostaję odpowiedź 200 - ok, ale lista jest pusta.

TicketRepository -> zwkle repo rezszerzone o jpaRepository. Testuje metode findAll() -> standardowa
Ticket -> ma 3 pola, gettery, settery
TicketController -> obsługa metody get i wywołanie w niej metody findAll() z TicketRepository -> najprostsze jakie może być

Sprawdziłem asercją samą metodę repozytorium po zamockowaniu i zwraca poprawnie ten 1 ticket, który zapodałem.
Jak użyję mockMvc do testu controllera, który pod adresem ".../tickets" wywołuje tylko metodę findAll() z TicketRepository.
Co robię źle, że lista w teście jest pusta, a powinna mieć jeden element?

Test wygląda następująco:

package com.AplikacjaKinowa.AplikacjaKinowa.service;

import com.AplikacjaKinowa.AplikacjaKinowa.controller.TicketController;
import com.AplikacjaKinowa.AplikacjaKinowa.model.Ticket;
import com.AplikacjaKinowa.AplikacjaKinowa.model.TicketRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(MockitoJUnitRunner.class)
@ExtendWith(MockitoExtension.class)
public class TicketControllerMvcTest {
    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext context;

    @Mock
    TicketRepository ticketRepository;

    @InjectMocks
    TicketController ticketController;


    @Before
    public void setUp()
    {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(ticketController).build();
    }

    @Test
    public void getTicketTest() throws Exception
    {
        Ticket ticket = new Ticket();
        ticket.setId(1);
        ticket.setPrice(111);
        ticket.setTicketType("BILECIK DO KONTROLI");


        List<Ticket> ticketList = new ArrayList<>();
        ticketList.add(ticket);
        given(ticketRepository.findAll()).willReturn(ticketList);


        MvcResult result = mockMvc.perform(get("/tickets").content(MediaType.APPLICATION_JSON_VALUE))
                .andExpect(status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(1)))
                .andReturn();

        String resultContent = result.getResponse().getContentAsString();
        System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: "+resultContent);

    }
}

No i dostaję po odpaleniu testu coś takiego:

10:36:12.213 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Log4j2LoggerProvider
10:36:12.216 [main] INFO org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 6.1.5.Final
10:36:12.238 [main] DEBUG org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator - Loaded expression factory via original TCCL
10:36:12.239 [main] DEBUG org.hibernate.validator.internal.engine.AbstractConfigurationImpl - Setting custom MessageInterpolator of type org.springframework.validation.beanvalidation.LocaleContextMessageInterpolator
10:36:12.240 [main] DEBUG org.hibernate.validator.internal.engine.AbstractConfigurationImpl - Setting custom ParameterNameProvider of type org.springframework.validation.beanvalidation.LocalValidatorFactoryBean$1
10:36:12.245 [main] DEBUG org.hibernate.validator.internal.xml.config.ValidationXmlParser - Trying to load META-INF/validation.xml for XML based Validator configuration.
10:36:12.246 [main] DEBUG org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via TCCL
10:36:12.246 [main] DEBUG org.hibernate.validator.internal.xml.config.ResourceLoaderHelper - Trying to load META-INF/validation.xml via Hibernate Validator's class loader
10:36:12.247 [main] DEBUG org.hibernate.validator.internal.xml.config.ValidationXmlParser - No META-INF/validation.xml found. Using annotation based configuration only.
10:36:12.254 [main] DEBUG org.hibernate.validator.internal.engine.resolver.TraversableResolvers - Found javax.persistence.Persistence on classpath containing 'getPersistenceUtil'. Assuming JPA 2 environment. Trying to instantiate JPA aware TraversableResolver
10:36:12.255 [main] DEBUG org.hibernate.validator.internal.engine.resolver.TraversableResolvers - Instantiated JPA aware TraversableResolver of type org.hibernate.validator.internal.engine.resolver.JPATraversableResolver.
10:36:12.365 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000252: Using org.hibernate.validator.internal.engine.DefaultPropertyNodeNameProvider as property node name provider.
10:36:12.371 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.springframework.validation.beanvalidation.LocaleContextMessageInterpolator as ValidatorFactory-scoped message interpolator.
10:36:12.371 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.resolver.JPATraversableResolver as ValidatorFactory-scoped traversable resolver.
10:36:12.371 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.util.ExecutableParameterNameProvider as ValidatorFactory-scoped parameter name provider.
10:36:12.371 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.DefaultClockProvider as ValidatorFactory-scoped clock provider.
10:36:12.371 [main] DEBUG org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper - HV000234: Using org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory as ValidatorFactory-scoped script evaluator factory.
10:36:12.476 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - 4 mappings in <unknown>
10:36:12.672 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
10:36:12.699 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice
10:36:12.739 [main] INFO org.springframework.mock.web.MockServletContext - Initializing Spring TestDispatcherServlet ''
10:36:12.740 [main] INFO org.springframework.test.web.servlet.TestDispatcherServlet - Initializing Servlet ''
10:36:12.741 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Detected AcceptHeaderLocaleResolver
10:36:12.741 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Detected FixedThemeResolver
10:36:12.742 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Detected org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@5e048149
10:36:12.742 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Detected org.springframework.web.servlet.support.SessionFlashMapManager@79d9214d
10:36:12.742 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
10:36:12.742 [main] INFO org.springframework.test.web.servlet.TestDispatcherServlet - Completed initialization in 2 ms
10:36:12.783 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - GET "/tickets", parameters={}
10:36:12.786 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.AplikacjaKinowa.AplikacjaKinowa.controller.TicketController#getTicket()
10:36:12.796 [main] INFO com.AplikacjaKinowa.AplikacjaKinowa.controller.TicketController - TICKETS: SHOW ALL TICKETS - GET METHOD
10:36:12.808 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Using 'application/json', given [*/*] and supported [application/json, application/*+json]
10:36:12.809 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor - Writing [[]]
10:36:12.823 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Completed 200 OK
10:36:12.860 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $

java.lang.AssertionError: JSON path "$"
Expected: a collection with size <1>
     but: collection size was <0>

	at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:18)
	at org.springframework.test.util.JsonPathExpectationsHelper.assertValue(JsonPathExpectationsHelper.java:73)
	at org.springframework.test.web.servlet.result.JsonPathResultMatchers.lambda$value$0(JsonPathResultMatchers.java:87)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
	at com.AplikacjaKinowa.AplikacjaKinowa.service.TicketControllerMvcTest.getTicketTest(TicketControllerMvcTest.java:77)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:567)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:46)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:77)
	at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:83)
	at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
	at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)


Process finished with exit code -1

0
Batman109 napisał(a):

Uczę się robienia testów aplikacji z wykorzystaniem JUnit i Mockito. Próbuję zrobić test controllera przy pomocy mockMvc, ale nie mogę dojść do tego co jest nie tak.

Jeśli na prawdę uczysz się pisać dopiero testy automatyczne, to rozpoczęcie tej nauki o testów aplikacji springowych z JPA to jest najtrudniejsza i najgorsza rzecz jaką możesz zrobić.

Zacznij napisanie testu o prostej aplikacji, bez springa, bez bazy, która tylko wpuszcza i wypuszcza dane. Napisz tak chociaż 30 testów, i zacznij dalszą naukę od tego.

Testy aplikacji HTTP z bazą są bardzo trudne żeby je zrobić dobrze; i bardzo prosto jest sobie wyrobić masę złych nawyków.

0

Pokarz jak wygląda TicketController

0
KamilAdam napisał(a):

Pokarz jak wygląda TicketController

@RestController
@RequestMapping("/tickets")
public class TicketController {

    private final ApplicationEventPublisher eventPublisher;
    private static final Logger logger = LoggerFactory.getLogger(TicketController.class);
    private final TicketRepository ticketRepository;

    public TicketController(final ApplicationEventPublisher eventPublisher, final TicketRepository ticketRepository) {
        this.eventPublisher = eventPublisher;
        this.ticketRepository = ticketRepository;
    }

    //GET METHOD - TICKET - SMALL - TEN PRZYPADEK TESTUJE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    @GetMapping()
    public List<Ticket> getTicket()
    {
        logger.info("TICKETS: SHOW ALL TICKETS - GET METHOD");
        return ticketRepository.findAll();
    }
}
1

Ten controller nie ma w sobie logiki nie ma tutaj co testować.

Jeśli chcesz napisać dobry test, to powinieneś zrobić test który strzela pod dwa endpointy, coś w stylu

(pseudokod)

post("/ticket", singletonMap("name", "mój ticket"));
ticket = get("/ticket/1");
assertEquals("mój ticket", ticket.name);

I sugerowałbym bez mockito, tylko np wbudowany test client od springa i h2 albo baza in-memory.

0
Riddle napisał(a):

Ten controller nie ma w sobie logiki nie ma tutaj co testować.

Jeśli chcesz napisać dobry test, to powinieneś zrobić test który strzela pod dwa endpointy, coś w stylu

(pseudokod)

post("/ticket", singletonMap("name", "mój ticket"));
ticket = get("/ticket/1");
assertEquals("mój ticket", ticket.name);

Tak ja wiem, że nic takiego nie ma w nim.
Zrobiłem wcześniej parę testów i chciałem zobaczyć pierwszy raz jak to działa z mockmvc, nie działało więc wybrałem sobie najprostszy przypadek gdzie oprę się o przykłady w internetowych tutorialach i gdzie teoretycznie nic się nie powinno rozkraczyć.

Chciałbym zrozumieć co się dzieje, że tam w teście zwraca mi listę z zerową liczbą elementów. Bo dla mnie ten kod wygląda logicznie i nie bardzo rozumiem to zachowanie.

0
Batman109 napisał(a):

Tak ja wiem, że nic takiego nie ma w nim.
Zrobiłem wcześniej parę testów i chciałem zobaczyć pierwszy raz jak to działa z mockmvc, nie działało więc wybrałem sobie najprostszy przypadek gdzie oprę się o przykłady w internetowych tutorialach i gdzie teoretycznie nic się nie powinno rozkraczyć.

Chciałbym zrozumieć co się dzieje, że tam w teście zwraca mi listę z zerową liczbą elementów. Bo dla mnie ten kod wygląda logicznie i nie bardzo rozumiem to zachowanie.

Wybrałeś sobie średnio dobry punkt żeby zacząć, bo mockmvc ze springa to nie jest dobry przykład tego jak powinno się pisać takie testy. Dużo lepiej byłoby gdybyś zaczął od pisania testów do prostych programów, takich które nie mają tyle boundries jak aplikacje webowe.

1 użytkowników online, w tym zalogowanych: 0, gości: 1