Замена Mocked Spring Boot Controller на фактический контроллер - PullRequest
0 голосов
/ 03 октября 2018

Я новичок в Spring Boot and Testing.

tl; dr Как заменить контроллер @MockBean на фактический контроллер в приложении с загрузочной пружиной, чтобы я мог проверить, работает ли контроллервместо того, чтобы просто проверить, что мои объекты выводятся правильно?

Я пишу управляемый Gradle API с зависимостями (из build.gradle):

// Spring Boot (2.0.5 Release)
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-hateoas')
compile('org.springframework.boot:spring-boot-starter-web')
runtime('org.springframework.boot:spring-boot-devtools')

// Testing
testImplementation('org.junit.jupiter:junit-jupiter-api:5.3.1')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.3.1')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile("org.assertj:assertj-core:3.11.1")
testCompile 'org.mockito:mockito-core:2.+'

У меня есть контроллер APIкласс со следующим соответствующим кодом:

@Controller
public class ObjectivesApiController extends AbstractRestHelperFunctionality implements ObjectivesApi {

protected ObjectivesApiController(
        UserRepository userRepository,
        CompaniesRepository companiesRepository,
        TeamsRepository teamsRepository,
        ProjectsRepository projectsRepository,
        OdiAssessmentRepository odiAssessmentRepository,
        OdiCustomerRatingRepository odiCustomerRatingRepository,
        OdiTechRatingRepository odiTechRatingRepository,
        OdiValueRatingRepository odiValueRatingRepository,
        ObjectivesRepository objectivesRepository,
        KeyResultRepository keyResultRepository) {
    super(
            userRepository,
            companiesRepository,
            teamsRepository,
            projectsRepository,
            odiAssessmentRepository,
            odiCustomerRatingRepository,
            odiTechRatingRepository,
            odiValueRatingRepository,
            objectivesRepository,
            keyResultRepository);
}

public ResponseEntity<KeyResult> createKeyResult(@ApiParam(value = "id", required = true) @PathVariable("id") Long id, @ApiParam(value = "keyResult", required = true) @Valid @RequestBody KeyResult keyResultDTO) {

    KeyResult keyResult = KeyResultBuilder
            .aKeyResult()
            .withDescription(keyResultDTO.getDescription())
            .withCompleted(keyResultDTO.getCompleted())
            .build();

    Objective parentObjective = objectivesRepository.findByObjectiveId(id);
    parentObjective.addKeyResult(keyResult);
    keyResultRepository.save(keyResult);
    objectivesRepository.save(parentObjective);

    return new ResponseEntity<KeyResult>(HttpStatus.CREATED);
}

public ResponseEntity<Objective> createObjective(@ApiParam(value = "objective", required = true) @Valid @RequestBody Objective objectiveDTO) {

    Objective objective = ObjectiveBuilder
            .anObjective()
            .withDescription(objectiveDTO.getDescription())
            .withCompleted(objectiveDTO.getCompleted())
            .withKeyResults(objectiveDTO.getKeyResults())
            .build();

    objective.getKeyResults().forEach(keyResultRepository::save);

    objectivesRepository.save(objective);
    return new ResponseEntity<Objective>(HttpStatus.CREATED);
}

public ResponseEntity<Void> deleteAllLinkedKeyResults(@ApiParam(value = "id", required = true) @PathVariable("id") Long id) {
    Objective subjectObjective = objectivesRepository.findByObjectiveId(id);

    subjectObjective.getKeyResults().clear();
    objectivesRepository.save(subjectObjective);

    return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}

public ResponseEntity<Void> deleteObjective(@ApiParam(value = "id", required = true) @PathVariable("id") Long id) {
    objectivesRepository.delete(objectivesRepository.findByObjectiveId(id));
    return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}

public ResponseEntity<Void> deleteOneKeyResult(@ApiParam(value = "the id of the objective you want key results for", required = true) @PathVariable("objectiveId") Long objectiveId, @ApiParam(value = "the id of the key result", required = true) @PathVariable("keyResultId") Long keyResultId) {
    Objective subjectObjective = objectivesRepository.findByObjectiveId(objectiveId);
    KeyResult keyResult = keyResultRepository.findByKeyResultId(keyResultId);

    subjectObjective.removeKeyResult(keyResult);

    objectivesRepository.save(subjectObjective);
    keyResultRepository.delete(keyResult);

    return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}

public ResponseEntity<List<Objective>> getAllObjectives() {
    List<Objective> allObjectives = objectivesRepository.findAll();
    return new ResponseEntity<List<Objective>>(allObjectives, HttpStatus.OK);
}

public ResponseEntity<List<KeyResult>> getKeyResultsForObjective(@ApiParam(value = "id", required = true) @PathVariable("id") Long id) {
    Objective subjectObjective = objectivesRepository.findByObjectiveId(id);
    List<KeyResult> allKeyResults = subjectObjective.getKeyResults();
    return new ResponseEntity<List<KeyResult>>(allKeyResults, HttpStatus.OK);
}

public ResponseEntity<Objective> getObjective(@ApiParam(value = "id", required = true) @PathVariable("id") Long id) {
    Objective subjectObjective = objectivesRepository.findByObjectiveId(id);
    return new ResponseEntity<Objective>(subjectObjective, HttpStatus.OK);
}

public ResponseEntity<KeyResult> getKeyResultForObjective(@ApiParam(value = "the id of the objective you want key results for", required = true) @PathVariable("objectiveId") Long objectiveId, @ApiParam(value = "the id of the key result", required = true) @PathVariable("keyResultId") Long keyResultId) {
    Objective subjectObjective = objectivesRepository.findByObjectiveId(objectiveId);
    KeyResult subjecKeyResult = subjectObjective.getKeyResults().stream()
            .filter(KeyResult -> keyResultId.equals(KeyResult.getKeyResultId()))
            .findFirst()
            .orElse(null);

    return new ResponseEntity<KeyResult>(subjecKeyResult, HttpStatus.OK);
}

public ResponseEntity<Objective> updateObjective(@ApiParam(value = "id", required = true) @PathVariable("id") Long id, @ApiParam(value = "objective", required = true) @Valid @RequestBody Objective objectiveDTO) {

    Objective existingObjective = objectivesRepository.findByObjectiveId(id);

    Objective objective = ObjectiveBuilder
            .anObjective()
            .withObjectiveId(existingObjective.getObjectiveId())
            .withDescription(objectiveDTO.getDescription())
            .withCompleted(objectiveDTO.getCompleted())
            .withKeyResults(objectiveDTO.getKeyResults())
            .build();

    objective.getKeyResults().forEach(keyResultRepository::save);

    objectivesRepository.save(objective);
    return new ResponseEntity<Objective>(HttpStatus.NO_CONTENT);
}

public ResponseEntity<KeyResult> updateKeyResult(@ApiParam(value = "the id of the objective you want key results for", required = true) @PathVariable("objectiveId") Long objectiveId, @ApiParam(value = "the id of the key result", required = true) @PathVariable("keyResultId") Long keyResultId, @ApiParam(value = "keyResult", required = true) @Valid @RequestBody KeyResult keyResultDTO) {
    if (objectivesRepository.existsById(objectiveId) && keyResultRepository.existsById(keyResultId)) {
        Objective subjectObjective = objectivesRepository.findByObjectiveId(objectiveId);

        KeyResult subjecKeyResult = subjectObjective.getKeyResults().stream()
                .filter(KeyResult -> keyResultId.equals(KeyResult.getKeyResultId()))
                .findFirst()
                .orElse(null);

        KeyResult updatedKeyResult = KeyResultBuilder
                .aKeyResult()
                .withKeyResultId(subjecKeyResult.getKeyResultId())
                .withDescription(keyResultDTO.getDescription())
                .withCompleted(keyResultDTO.getCompleted())
                .build();

        keyResultRepository.save(updatedKeyResult);

        Collections.replaceAll(subjectObjective.getKeyResults(), subjecKeyResult, updatedKeyResult);

        objectivesRepository.save(subjectObjective);
    }

    return new ResponseEntity<KeyResult>(HttpStatus.NO_CONTENT);
}

}

Для контекста этого класса все, что делает суперкласс AbstractRestHelper, создает синглтоны из моих репозиториев, которые затем вставляются в поле .. (не уверен, если этоправильный термин) в контроллере.Этот шаблон повторяется на всех контроллерах, следовательно, беспорядок.

Реализуемый API - это интерфейс API Swagger 2, который по возможности защищает этот контроллер от аннотаций.

Последний элемент - это тестовый класс,Это суть моего вопроса.

@ExtendWith(SpringExtension.class)
@WebMvcTest(ObjectivesApiController.class)
class ObjectivesApiControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ObjectivesApiController objectivesApiControllerMock;

    @BeforeEach
    void setUp() {
    }

    @AfterEach
    void tearDown() {
    }

    @Test
    void getAllObjectives() throws Exception {
        // Create two objects to test with:

        Objective testObjective1 = ObjectiveBuilder
                .anObjective()
                .withObjectiveId(1L)
                .withDescription("Test Objective")
                .withCompleted(false)
                .build();

        Objective testObjective2 = ObjectiveBuilder
                .anObjective()
                .withObjectiveId(2L)
                .withDescription("Test Objective")
                .withCompleted(true)
                .build();

        List<Objective> testList = new ArrayList<Objective>();
        testList.add(testObjective1);
        testList.add(testObjective2);

        // Set expectations on what should be found:
        when(objectivesApiControllerMock.getAllObjectives()).thenReturn(new ResponseEntity<List<Objective>>(testList, HttpStatus.OK));

        // Carry out the mocked API call:
        mockMvc.perform(get("/objectives"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].objectiveId", is(1)))
                .andExpect(jsonPath("$[0].description", is("Test Objective")))
                .andExpect(jsonPath("$[0].completed", is(false)))
                .andExpect(jsonPath("$[1].objectiveId", is(2)))
                .andExpect(jsonPath("$[1].description", is("Test Objective")))
                .andExpect(jsonPath("$[1].completed", is(true)));


        // Validate the response is what we expect:
        verify(objectivesApiControllerMock, times(1)).getAllObjectives();
        verifyNoMoreInteractions(objectivesApiControllerMock);

    }

    @Test
    void getKeyResultsForObjective() throws Exception {

        KeyResult testKeyResultWithParentObjective1 = KeyResultBuilder
                .aKeyResult()
                .withKeyResultId(1L)
                .withCompleted(false)
                .withDescription("My parent Key Result is 1")
                .build();

        KeyResult testKeyResultWithParentObjective2 = KeyResultBuilder
                .aKeyResult()
                .withKeyResultId(2L)
                .withCompleted(true)
                .withDescription("My parent Key Result is 1")
                .build();

        Objective testObjectiveWithKeyResults = ObjectiveBuilder
                .anObjective()
                .withObjectiveId(1L)
                .withDescription("Test Objective")
                .withKeyResults(new ArrayList<KeyResult>())
                .withCompleted(false)
                .build();

        testObjectiveWithKeyResults.addKeyResult(testKeyResultWithParentObjective1);
        testObjectiveWithKeyResults.addKeyResult(testKeyResultWithParentObjective2);

        when(objectivesApiControllerMock.getKeyResultsForObjective(1L)).thenReturn(new ResponseEntity<List<KeyResult>>(testObjectiveWithKeyResults.getKeyResults(), HttpStatus.OK));

        mockMvc.perform(get("/objectives/1/keyresult"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].keyResultId", is(1)))
                .andExpect(jsonPath("$[0].description", is("My parent Key Result is 1")))
                .andExpect(jsonPath("$[0].completed", is(false)))
                .andExpect(jsonPath("$[1].keyResultId", is(2)))
                .andExpect(jsonPath("$[1].description", is("My parent Key Result is 1")))
                .andExpect(jsonPath("$[1].completed", is(true)));

    }
}

Мой вопрос таков: после того, как Mockito проверил объектный контроллер на предмет правильности формирования моих объектов, я теперь хочу сделать то же самое, но вместоЯ хочу на самом деле протестировать контроллер.

Как вы думаете, какой самый наивный способ заставить это работать (я могу реорганизовать позже).Ресурсы, которые я искал, используют либо разные версии Junit, либо используют mockito, а не реальный контроллер.

Ничто не подходит совершенно правильно - поскольку контроллер смоделирован, я фактически не рассматриваю какой-либо код, итак что тесты бесполезны, верно?Единственное, на что я смотрю, это на то, что объекты сформированы правильно, и теперь мне нужно проверить, работает ли контроллер должным образом, и возвращают ли правильно сформированные объекты.

Кто-нибудь делал что-нибудь похожее?Какие подходы вы используете для управления тестированием полевых контроллеров?

Любой совет по этому вопросу будет очень ценным.Мне бы очень хотелось узнать, как люди, работающие с приложениями промышленного уровня, проводят тестирование Spring Boot Apps с контроллерами, Repos и т. Д.

Большое спасибо!

1 Ответ

0 голосов
/ 03 октября 2018

Вы можете использовать @SpyBean.Таким образом, вы можете использовать его как есть или издеваться над некоторыми звонками.https://www.baeldung.com/mockito-spy

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...