使用 Mockito 进行测试

Testing with Mockito

我正在尝试使用 Mockito 测试一些服务,但是当我测试的主要 class 以及我将 Mocks 调用注入 super 时,我遇到了问题。 我用 spring 运行项目,这些是我遇到错误时遵循的步骤。

这是我创建测试的地方

public class UrlShortenerTests {

 private MockMvc mockMvc;

 @Mock
 private ShortURLRepository shortURLRepository;

 @Mock
 private ClickRepository clickRespository;

 @InjectMocks
 private UrlShortenerControllerWithLogs urlShortenerWL;


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

 @Test
 public void thatShortenerCreatesARedirectIfTheURLisOK() throws Exception {
     mockMvc.perform(post("/link")
             .param("url", "http://www.google.com"))
             .andDo(print())
             .andExpect(status().isCreated())
             .andExpect(jsonPath("$.target", is("http://example.com/")));
 }
}

这是带有方法缩短器的 class UrlShortenerControllerWithLogs,这是我想用之前的 POST 调用

测试的那个
@RestController
public class UrlShortenerControllerWithLogs extends UrlShortenerController {

@Autowired
private ClickRepository clickRepository;
@Autowired
private ShortURLRepository SURLR;

public ResponseEntity<ShortURL> shortener(@RequestParam("url") String url,
        @RequestParam(value = "sponsor", required = false) String sponsor,
        @RequestParam(value = "brand", required = false) String brand,
        HttpServletRequest request) {

    ResponseEntity<ShortURL> su = super.shortener(url, sponsor, brand,
            request);
    return su;
}

这就是超级class

@RestController
public class UrlShortenerController {

@Autowired
protected ShortURLRepository shortURLRepository;

@Autowired
protected ClickRepository clickRepository;

@RequestMapping(value = "/link", method = RequestMethod.POST)
public ResponseEntity<ShortURL> shortener(@RequestParam("url") String url,
        @RequestParam(value = "sponsor", required = false) String sponsor,
        @RequestParam(value = "brand", required = false) String brand,
        HttpServletRequest request) {
    ShortURL su = createAndSaveIfValid(url, sponsor, brand, UUID
            .randomUUID().toString(), extractIP(request));
    if (su != null) {
        HttpHeaders h = new HttpHeaders();
        h.setLocation(su.getUri());
        return new ResponseEntity<>(su, h, HttpStatus.CREATED);
    } else {
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}

protected ShortURL createAndSaveIfValid(String url, String sponsor,
        String brand, String owner, String ip) {
    UrlValidator urlValidator = new UrlValidator(new String[] { "http",
            "https" });
    if (urlValidator.isValid(url)) {
        String id = Hashing.murmur3_32()
                .hashString(url, StandardCharsets.UTF_8).toString();
        ShortURL su = new ShortURL(id, url,
                linkTo(
                        methodOn(UrlShortenerController.class).redirectTo(
                                id, null)).toUri(), sponsor, new Date(
                        System.currentTimeMillis()), owner,
                HttpStatus.TEMPORARY_REDIRECT.value(), true, ip, null);
        return shortURLRepository.save(su);
    } else {
        return null;
    }
}

所以,当我在第二个方法 (createAndSaveIfValid) 中调用 shortURLRepository.save(su) 时,它永远不会进入方法保存,所以它 returns me null 而不是我想要的对象.

ShortURLRepository的实现代码和保存方法为:

@Repository
public class ShortURLRepositoryImpl implements ShortURLRepository {

private static final Logger log = LoggerFactory
        .getLogger(ShortURLRepositoryImpl.class);
@Override
public ShortURL save(ShortURL su) {
    try {
        jdbc.update("INSERT INTO shorturl VALUES (?,?,?,?,?,?,?,?,?)",
                su.getHash(), su.getTarget(), su.getSponsor(),
                su.getCreated(), su.getOwner(), su.getMode(), su.getSafe(),
                su.getIP(), su.getCountry());
    } catch (DuplicateKeyException e) {
        log.debug("When insert for key " + su.getHash(), e);
        return su;
    } catch (Exception e) {
        log.debug("When insert", e);
        return null;
    }
    return su;
}

我认为问题是在测试 class 中创建的对象 ShortURLRepository 没有在超级 class (UrlShortenerController) 或类似的东西上初始化。

可能吗? 有人可以帮我吗?

完整代码在:https://github.com/alberto-648702/UrlShortener2014

class UrlShortenerTests 在:

bangladeshGreen/src/test/java/urlshortener2014/bangladeshgreen

class UrlShortenerControllerWithLogs 在:

bangladeshGreen/src/main/java/urlshortener2014/bangladeshgreen/web

class UrlShortenerController 在:

common/src/main/java/urlshortener2014/common/web

class ShortURLRepositoryImpl 位于:

common/src/main/java/urlshortener2014/common/repository

这不是错误。这是预期的行为。 @Mock 创建一个模拟。 @InjectMocks 创建 class 的实例并注入使用 @Mock 创建的模拟。模拟不是具有已知值和方法的真实对象。它是一个对象,与声明的类型具有相同的接口,但您可以控制它的行为。默认情况下,模拟对象方法什么都不做(例如 return null)。因此,如果 ShortURLRepositoryUrlShortenerControllerWithLogs 中被模拟和注入,在注入的 ShortURLRepository 中调用 save 不会像您预期的那样调用真实代码,它什么也不做。如果您想模拟 save 的行为,请在 setup 中添加以下代码:

when(shortURLRepository.save(org.mockito.Matchers.any(ShortURL.class))).
    then(new Answer<ShortURL>() {
        @Override
        public ShortURL answer(InvocationOnMock invocation) throws Throwable {
            ShortURL su = (ShortURL) invocation.getArguments()[0];
            // Do something with su if needed
            return su;
    }
});