<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki.riguz.com/index.php?action=history&amp;feed=atom&amp;title=Blog%3A%E4%BD%BF%E7%94%A8TDD%E5%BC%80%E5%8F%91SpringBoot%E5%BA%94%E7%94%A8</id>
	<title>Blog:使用TDD开发SpringBoot应用 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.riguz.com/index.php?action=history&amp;feed=atom&amp;title=Blog%3A%E4%BD%BF%E7%94%A8TDD%E5%BC%80%E5%8F%91SpringBoot%E5%BA%94%E7%94%A8"/>
	<link rel="alternate" type="text/html" href="https://wiki.riguz.com/index.php?title=Blog:%E4%BD%BF%E7%94%A8TDD%E5%BC%80%E5%8F%91SpringBoot%E5%BA%94%E7%94%A8&amp;action=history"/>
	<updated>2026-06-02T20:39:35Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.42.3</generator>
	<entry>
		<id>https://wiki.riguz.com/index.php?title=Blog:%E4%BD%BF%E7%94%A8TDD%E5%BC%80%E5%8F%91SpringBoot%E5%BA%94%E7%94%A8&amp;diff=2561&amp;oldid=prev</id>
		<title>imported&gt;Riguz：​虽然觉得TDD没什么卵用，但实际工作中还是必须要使用TDD，这不最近就做了一个使用TDD的方式开发SpringBoot的例子。</title>
		<link rel="alternate" type="text/html" href="https://wiki.riguz.com/index.php?title=Blog:%E4%BD%BF%E7%94%A8TDD%E5%BC%80%E5%8F%91SpringBoot%E5%BA%94%E7%94%A8&amp;diff=2561&amp;oldid=prev"/>
		<updated>2019-09-04T00:00:00Z</updated>

		<summary type="html">&lt;p&gt;虽然觉得TDD没什么卵用，但实际工作中还是必须要使用TDD，这不最近就做了一个使用TDD的方式开发SpringBoot的例子。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;虽然觉得TDD没什么卵用，但实际工作中还是必须要使用TDD，这不最近就做了一个使用TDD的方式开发SpringBoot的例子。&lt;br /&gt;
&lt;br /&gt;
下面阐述一下如何开发一个RestFul的GET请求，从数据库中读取数据并返回。&lt;br /&gt;
&lt;br /&gt;
= 创建Integration Test=&lt;br /&gt;
第一步可以从IntegrationTest开始，即模拟真实发送一个HTTP请求，然后检验返回的Response。简单起见，第一步只校验状态值：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@RunWith(SpringRunner.class)&lt;br /&gt;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)&lt;br /&gt;
public class IntegrationTest {&lt;br /&gt;
&lt;br /&gt;
  @Autowired&lt;br /&gt;
  private TestRestTemplate testClient;&lt;br /&gt;
&lt;br /&gt;
  @Test&lt;br /&gt;
  public void should_get_computer_list_when_call_list_computer_api() {&lt;br /&gt;
    ResponseEntity&amp;lt;List&amp;lt;ComputerDto&amp;gt;&amp;gt; response = testClient.exchange(&lt;br /&gt;
        &amp;quot;/computers&amp;quot;,&lt;br /&gt;
        HttpMethod.GET,&lt;br /&gt;
        null,&lt;br /&gt;
        new ParameterizedTypeReference&amp;lt;List&amp;lt;ComputerDto&amp;gt;&amp;gt;() {&lt;br /&gt;
        });&lt;br /&gt;
    assertEquals(HttpStatus.OK, response.getStatusCode());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
这里特意使用WebEnvironment.RANDOM_PORT以使得Spring启动一个接近真实的Server，来测试我们的请求。当然这个测试会挂了，因为Controller都还没写呢。所以下一个先来创建Controller，但是呢，TDD通常从测试开始写起，所以来测试Controller吧。&lt;br /&gt;
&lt;br /&gt;
= Controller Test=&lt;br /&gt;
&lt;br /&gt;
测试Controller就是单元测试了，不需要测试其他的组件（比如service什么的)。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@RunWith(SpringRunner.class)&lt;br /&gt;
@WebMvcTest(controllers = ComputerController.class)&lt;br /&gt;
public class ComputerControllerTest {&lt;br /&gt;
&lt;br /&gt;
  @Autowired&lt;br /&gt;
  private MockMvc mockMvc;&lt;br /&gt;
&lt;br /&gt;
  @Test&lt;br /&gt;
  public void should_get_a_list_when_get_computers() throws Exception {&lt;br /&gt;
    mockMvc.perform(MockMvcRequestBuilders.get(&amp;quot;/computers&amp;quot;))&lt;br /&gt;
        .andExpect(status().isOk());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
然后就是需要创建一个Controller，让测试可以过：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@RestController(&amp;quot;/computers&amp;quot;)&lt;br /&gt;
public class ComputerController {&lt;br /&gt;
&lt;br /&gt;
  @GetMapping&lt;br /&gt;
  public List&amp;lt;ComputerDto&amp;gt; getComputers() {&lt;br /&gt;
    return null;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
到这里基本上集成测试也可以过了。所以你可以先commit一次了。然后，当然我们不能把逻辑放到Controller里面啊，我们需要一个Service来处理业务逻辑。这个Service又会从数据库中读取数据。在用到Repository之前，我们可能需要先改一下我们的controller测试，因为到目前为止并没有校验实际的字段，只是校验了返回状态码，现在可以开始校验了：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
  @MockBean&lt;br /&gt;
  private ComputerService computerService;&lt;br /&gt;
&lt;br /&gt;
  @Test&lt;br /&gt;
  public void should_get_a_list_when_get_computers() throws Exception {&lt;br /&gt;
    given(computerService.getComputers())&lt;br /&gt;
        .willReturn(&lt;br /&gt;
            Collections.singletonList(&lt;br /&gt;
                new ComputerDto(1, &amp;quot;MacBook 2015&amp;quot;, &amp;quot;Haifeng Li&amp;quot;, &amp;quot;2019-09-10&amp;quot;)&lt;br /&gt;
            ));&lt;br /&gt;
    mockMvc.perform(MockMvcRequestBuilders.get(&amp;quot;/computers&amp;quot;))&lt;br /&gt;
        .andExpect(status().isOk())&lt;br /&gt;
        .andExpect(jsonPath(&amp;quot;$&amp;quot;, hasSize(1)))&lt;br /&gt;
        .andExpect(jsonPath(&amp;quot;$[0].id&amp;quot;).value(1))&lt;br /&gt;
        .andExpect(jsonPath(&amp;quot;$[0].type&amp;quot;).value(&amp;quot;MacBook 2015&amp;quot;))&lt;br /&gt;
        .andExpect(jsonPath(&amp;quot;$[0].owner&amp;quot;).value(&amp;quot;Haifeng Li&amp;quot;))&lt;br /&gt;
        .andExpect(jsonPath(&amp;quot;$[0].createTime&amp;quot;).value(&amp;quot;2019-09-10&amp;quot;))&lt;br /&gt;
        .andDo(print());&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
这里我们把Service给Mock掉，因此可以控制它的行为，实际的Service就一个空函数就可以了。&lt;br /&gt;
&lt;br /&gt;
= Service=&lt;br /&gt;
这时候，可以考虑实现Service了，因为Service需要读取数据库，所以Service需要引入一个Repository来查询数据库，我们可以Mock掉Repository，来测service的逻辑：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@RunWith(MockitoJUnitRunner.class)&lt;br /&gt;
public class ComputerServiceTest {&lt;br /&gt;
&lt;br /&gt;
  @Mock&lt;br /&gt;
  private ComputerRepository computerRepository;&lt;br /&gt;
&lt;br /&gt;
  @InjectMocks&lt;br /&gt;
  private ComputerService computerService;&lt;br /&gt;
&lt;br /&gt;
  @Test&lt;br /&gt;
  public void should_return_computer_list_when_get_all_computers() throws ParseException {&lt;br /&gt;
    ComputerEntity stored = new ComputerEntity(1,&lt;br /&gt;
        &amp;quot;MacBook 2015&amp;quot;,&lt;br /&gt;
        &amp;quot;Haifeng Li&amp;quot;,&lt;br /&gt;
        new SimpleDateFormat(&amp;quot;dd/MM/yyyy&amp;quot;).parse(&amp;quot;01/09/2019&amp;quot;));&lt;br /&gt;
    given(computerRepository.findAll()).willReturn(Collections.singletonList(stored));&lt;br /&gt;
&lt;br /&gt;
    List&amp;lt;ComputerDto&amp;gt; computers = computerService.getComputers();&lt;br /&gt;
&lt;br /&gt;
    assertEquals(1, computers.size());&lt;br /&gt;
    assertEquals(1, computers.get(0).getId());&lt;br /&gt;
    assertEquals(&amp;quot;MacBook 2015&amp;quot;, computers.get(0).getType());&lt;br /&gt;
    assertEquals(&amp;quot;Haifeng Li&amp;quot;, computers.get(0).getOwner());&lt;br /&gt;
    assertEquals(&amp;quot;2019-09-01&amp;quot;, computers.get(0).getCreateTime());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
同样Repository里面也就一个空函数就行了，但是这时候得把Service 的逻辑写完，让测试可以通过，这样Service的任务就完成了，其他测试也全部都可以通过。&lt;br /&gt;
&lt;br /&gt;
= Repository=&lt;br /&gt;
&lt;br /&gt;
最后一步就是来实现Repository了，这里需要使用DataJpaTest，用内存数据库进行测试：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
@RunWith(SpringRunner.class)&lt;br /&gt;
@DataJpaTest&lt;br /&gt;
public class ComputerRepositoryTest {&lt;br /&gt;
&lt;br /&gt;
  @Autowired&lt;br /&gt;
  private TestEntityManager entityManager;&lt;br /&gt;
&lt;br /&gt;
  @Autowired&lt;br /&gt;
  private ComputerRepository computerRepository;&lt;br /&gt;
&lt;br /&gt;
  @Before&lt;br /&gt;
  public void prepareData() throws ParseException {&lt;br /&gt;
    entityManager.persistAndFlush(new ComputerEntity(1,&lt;br /&gt;
        &amp;quot;MacBook 2015&amp;quot;,&lt;br /&gt;
        &amp;quot;Haifeng Li&amp;quot;,&lt;br /&gt;
        new SimpleDateFormat(&amp;quot;dd/MM/yyyy&amp;quot;).parse(&amp;quot;01/09/2019&amp;quot;))&lt;br /&gt;
    );&lt;br /&gt;
    entityManager.persistAndFlush(new ComputerEntity(2,&lt;br /&gt;
        &amp;quot;Desktop&amp;quot;,&lt;br /&gt;
        null,&lt;br /&gt;
        new SimpleDateFormat(&amp;quot;dd/MM/yyyy&amp;quot;).parse(&amp;quot;02/09/2019&amp;quot;))&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  @Test&lt;br /&gt;
  public void should_return_all_records_in_db_when_find_all() {&lt;br /&gt;
    List&amp;lt;ComputerEntity&amp;gt; entities = computerRepository.findAll();&lt;br /&gt;
    assertEquals(2, entities.size());&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
因为Spring JPA你只需要写一堆interface，测试这里的逻辑还是十分有必要的。所以到这一步为止，基本上程序的功能就已经实现了，唯一需要做的就是改一下配置来连接到真是的数据库。整个代码可以在我的[https://github.com/soleverlee/computer-inventory.git Github]上面找到。&lt;/div&gt;</summary>
		<author><name>imported&gt;Riguz</name></author>
	</entry>
</feed>