为什么要写单元测试?
透过单元测试,验证开发的结果最终与预期的一致确保每段程式仅包含要验证的业务逻辑有了单元测试,我们对工作的定义範围就比较注重了,因为多做要多花时间嘛!于是我们就跟主管商量,这两週就做这些事,如果高层问起进度,就可以以两週为单位回报,主管发现这样他也很好做事,而且这群人交出来的东西真的不太会错,于是每次回报进度也就不用伤脑筋抓 Buffer 了。
敏捷圣徒 Day 1:我不是专家 (Kuma)
Unit Test 要不要包含 DB ?
你就是不写测试才会没时间 2-12 (Kuma)
取决 DB 的管理,若开发者都是共用同一个 DB ,此情况 DB 属于「外部系统」(视同呼叫第三方的 Api),故不包含测试,如果测试可以使用可以使用 H2 设定,此情况 DB 属于「内部系统」,则可以单元测试,总的来说,Unit Test 不管怎么定义,至少「不要牵扯外部系统」
单元测试
你就是不写测试才会没时间 2-2 (Kuma)
单元测试的起手三件套
expect 準备资料actual 执行程式verify 验证结果public class Student { private String firstName; private String lastName; public Student(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFullName() { return firstName + " " + lastName; }}
class StudentTest { @Test void full_name () { // expect Student student = new Student("Michael","Jordan"); // actual String actual = student.getFullName(); // verify assertEquals("Michael Jordan", actual) }}
控制依赖
你就是不写测试才会没时间 3-2 (Kuma)
public boolean checkTime(ApplicationForm applicationForm) { Scholarship scholarship = scholarshipRepository.find(applicationForm.getScholarshipId()); LocalDate deadline = scholarship.getDeadline(); LocalDate today = LocalDate.now(); return today.isEqual(deadline) || today.isBefore(deadline);}
出现以下三个依赖
Application
可以控制,直接 constructor 或 setter,控制 getter 的行为
ScholarshipRepository
无法控制,因为没有真实 DB,可以使用 Mockito,Mock DB 物件,并指定方法的输出内容
ScholarshipRepository fakeRepository = Mockito.mock(ScholarshipRepository.class);Mockito.when(fackRepository.find(777L)).thenReturn(new Scholarship(LocalDate.MAX);
LocalDate.now()
无法控制,因为是 static,会随着测试的準备阶段自动初始方法,无法提前 Mock 取代,不过后来版本的 Mockito 对 static 的支援,因此可以覆盖,如程式码举例
LocalDate expected = LocalDate.of(2029, 12, 31);Mockito.mockStatic(LocalDate.class).when(LocalDate::now).thenReturn(expected);
治本的方法,就是要意识到目前方法的实作与 static method LocalDate.now() 产生耦合,导致单元测试不容易撰写,应该重构方法,参考 Dependency Inversion 来解偶,重构如下
public boolean checkTime(Application application, Clock clock) { LocalDate today = clock.now(); // ...
LocalDate expected = LocalDate.of(2029, 12, 31);Clock fakeClock = Mockito.mock(Clock.class);Mockito.when(fackClock.now()).thenReturn(expected);
方法经过重构后,方法原本依赖系统的时间,改成输入自方法,方法的核心逻辑内聚提高,不受原本 LocalDate.now() 的实作引响
Mock 与 Stub
你就是不写测试才会没时间 3-5 (Kuma)
Stub
「可控的替代物件,用来取代一个外部相依物件」
如果我们先跟假物件串通好与待会要回传给待测物件值,最后再去检查待测物件的状态,那它就是Stub。
Mock
「一个假物件,用来验证待测物件是否如预期般呼叫这个假物件」
如果我们直接让带待物件互动,最后再去检查刚刚假物件与待测物件的互动情形,那它就是 Mock。