2

I'm experimenting with Spring Web and testing a REST controller. The application is basically a game database accessible through a web service.

When I launch it and test it with Postman to add a game, I get the behavior I'm looking for. However, when I test the controller with the SpringJUnit4ClassRunner, it seems the game I'm trying to add already exists in the database and I can't add it.

Here is my test class:

@RunWith(SpringJUnit4ClassRunner.class)
@WebMvcTest(GameController.class)
public class GameControllerTest {

    @MockBean
    private IGameService gameService;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void postGameTest() throws Exception {
        String mockGameJson = "{\"name\":\"Test Game\",\"description\":\"A test game.\"}";

        //Create a post request with an accept header for application\json
        RequestBuilder requestBuilder = MockMvcRequestBuilders
                .post("/game/")
                .accept(MediaType.APPLICATION_JSON).content(mockGameJson)
                .contentType(MediaType.APPLICATION_JSON);

        MvcResult result = mockMvc.perform(requestBuilder).andReturn();

        MockHttpServletResponse response = result.getResponse();

        //Assert that the return status is CREATED
        assertEquals(HttpStatus.CREATED.value(), response.getStatus());
    }
}

The assert in the last line fails because the status is a http 409 Conflict

I personally return that status in the controller:

@RestController
public class GameController {

    @Autowired
    private IGameService gameService;

    @PostMapping("/game")
    public ResponseEntity<String> addGame(@RequestBody Game game, UriComponentsBuilder builder) {
        boolean flag = gameService.addGame(game);
        if (!flag) return new ResponseEntity<>("Another game with this name already exists.", HttpStatus.CONFLICT);
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("/game/{id}").buildAndExpand(game.getId()).toUri());
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }
...

It doesn't make sense that that would happen because my database is supposed to be empty at the beginning of the test right? Here is the related service:

@Service
public class GameService implements IGameService { //Service layer

    @Autowired
    private IGameDAO gameDAO;

    @Override
    public synchronized boolean addGame(Game game) {
        if(gameDAO.gameExists(game.getName()))
            return false;
        else {
            gameDAO.addGame(game);
            return true;
        }
    }
...

And the DAO:

@Transactional
@Repository
public class GameDAO implements IGameDAO {

    @PersistenceContext
    private EntityManager entityManager;


    @Override
    public void addGame(Game game) {
        entityManager.persist(game);
    }

    @Override
    public boolean gameExists(String name) {
        String jpql = "from Game as g WHERE g.name = ?0 ";
        int count = entityManager.createQuery(jpql).setParameter(0, name).getResultList().size();
        return count > 0;
    } ...

And those are the dependencies in my build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'com.h2database:h2'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

What am i doing wrong here?

gaucib
  • 465
  • 4
  • 8
  • 1
    i think since you are using a @MockBean and not the actual bean, the autowired DAO repo is just mocked, not actually used. If you would want to test the Repo, test the repos methods explicitly in your tests. When testing spring apps, its wise to test every layer on its own(data, web, service). – Gewure Mar 23 '19 at 02:29

1 Answers1

0

Solved it. As it was commented, it is better to test the layers independently. Here, I was trying to test my controller (web). The service is mocked so by default any invocation on a method that should return a boolean would return false.

I had to tell the the mocked service to return true to adequately test the post method. Like that:

given(gameService.addGame(any())).willReturn(true);
gaucib
  • 465
  • 4
  • 8