最近在實(shí)踐測(cè)試闯冷,經(jīng)常遇到一個(gè)問(wèn)題砂心,就是測(cè)試怎么寫(xiě),測(cè)試粒度是怎么樣的蛇耀?所以狂補(bǔ)了一下測(cè)試相關(guān)的知識(shí)辩诞。
測(cè)試金字塔
測(cè)試金字塔將測(cè)試進(jìn)行了分層,越底層的測(cè)試纺涤,實(shí)現(xiàn)成本越低译暂,測(cè)試效率更高,可維護(hù)性越高撩炊,但是業(yè)務(wù)需求的反映越低
測(cè)試金字塔
如圖秧秉,測(cè)試被分成了三層,從下到上分成:Unit層衰抑,UI層和Service層象迎。
- Unit層則是我們常說(shuō)的單元測(cè)試。
- Service層又可以細(xì)分呛踊,如(集成測(cè)試砾淌,功能測(cè)試,系統(tǒng)測(cè)試谭网,契約測(cè)試汪厨,端到端測(cè)試)。
- UI層則是用戶(hù)界面測(cè)試愉择。
單元測(cè)試
單元測(cè)試處于金字塔最底部劫乱,這類(lèi)測(cè)試的特點(diǎn)是:
- 只測(cè)試單獨(dú)的class
- 只需要有應(yīng)用的源碼,而不需要跑服務(wù)實(shí)例
- 夠快
- 不依賴(lài)于外部系統(tǒng)(web service锥涕,database)且不需要很多/基本沒(méi)有I/O操作(不用建立數(shù)據(jù)庫(kù))
在Spring boot中主要有三種測(cè)試方法
- Controller測(cè)試 - 使用mockMvc
@RunWith(SpringRunner.class)
@WebMvcTest(TaskController.class)
public class TaskControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private TaskService taskService;
@Test
public void should_get_all_tasks_by_getAllTask() throws Exception {
given(taskService.getTasks())
.willReturn(Lists.newArrayList(createTask("test"), createTask("aaa")));
mockMvc.perform(MockMvcRequestBuilders.get("/tasks"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("test"))
.andExpect(jsonPath("$[1].name").value("aaa"));
}
public Task createTask(String name) {
Task task = new Task();
task.setName(name); return task;
}
}
- Service測(cè)試 - 使用Mockito
@RunWith(MockitoJUnitRunner.class)
public class TaskServiceTest {
@InjectMocks
private TaskService taskService;
@Mock
private TaskRepository taskRepository;
@Test
public void should_return_all_tasks_when_getAllTasks() {
given(taskRepository.findAll())
.willReturn(Lists.newArrayList(createTask("test"), createTask("aaa")));
List<Task> tasks = taskService.getTasks();
assertThat(tasks.size(), is(2));
assertThat(tasks.get(0).getName(), is("test"));
assertThat(tasks.get(1).getName(), is("aaa"));
}
public Task createTask(String name) {
Task task = new Task();
task.setName(name);
return task;
}
}
repository 測(cè)試 - 使用DataJpaTest
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class TaskRepositoryTest {
@Autowired
private TaskRepository taskRepository;
@Autowired
private TestEntityManager entityManager;
@Test
public void get_all_will_return_all_tasks() {
List<Task> tasks = taskRepository.findAll();
assertThat(tasks.size(), is(2));
assertThat(tasks.get(0).getName(), is("test"));
assertThat(tasks.get(1).getName(), is("aaa"));
}
}
基于H2衷戈,flywayTest annotation測(cè)試的寫(xiě)法。
@FlywayTest可以放在3個(gè)地方
- class上面:?jiǎn)蝹€(gè)class 會(huì)跑flywayclean 和flywaymigrate
- setup方法上面:每個(gè)測(cè)試都會(huì)跑flywayclean 和flywaymigrate
- 單個(gè)測(cè)試方法上面:?jiǎn)蝹€(gè)測(cè)試會(huì)跑flywayclean 和flywaymigrate
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
@RunWith(SpringRunner.class)
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
FlywayTestExecutionListener.class
})
class FlywayMethodTest {
@Autowired
private UserRepository repository;
@Test
@FlywayTest
void contextLoads() {
assertThat(repository.findAll().size(), is(3));
}
}