业务实战
业务需求
- 用户注册功能
- 其中用户姓名必填且不能重复
- …(为演示简便清晰,省略其他需求)
技术拆分(测试用例列表)
- 用户注册
- service层(这里的实现分为:controller、service、dao三层,因为service层依赖最少,我们先从service层开始)。
- 用户姓名为空时,用户注册失败(需求)。
- 1.使用用户姓名为空的实体添加用户,抛出RuntimeException(测试用例)。
- 用户姓名不为空时,用户注册成功(需求)。
- 2.使用用户姓名不为空的实体添加用户,保存成功,返回保存数量1(测试用例)。
- …(为演示简便清晰,省略其他测试用例)
- 用户姓名为空时,用户注册失败(需求)。
- dao层。
- …(为演示简便清晰,省略其他测试用例)
- controller层。
- …(为演示简便清晰,省略其他测试用例)
- service层(这里的实现分为:controller、service、dao三层,因为service层依赖最少,我们先从service层开始)。
代码实现
完成第一个测试用例的红绿蓝循环
- 测试用例:使用用户姓名为空的实体添加用户,抛出 RuntimeException。
- 实现:
- 创建测试用例类。
public class AccountServiceTest {
}
- 创建测试用例方法。
public class AccountServiceTest {
private AccountService accountService = new AccountServiceImpl();
@Test
public void shouldAddSuccess() {
//构造一个用户姓名为空的用户实体 : account
Account account = Account
.builder()
.name(null)
.build();
//期望当我们使用 account 创建对象时,抛出错误,错误类型为 RuntimeException.class
assertThatThrownBy(() -> accountService.add(account))
.isExactlyInstanceOf(RuntimeException.class);
}
}
由于此时接口类:AccountService、接口实现类:AccountServiceImpl、用户实体类:Account、add 方法均未实现,所以代码会报编译错误无法运行。
- 使测试用例代码可以运行。
我们可以通过快捷键,快速创建上面未实现的类以及方法。
3.1创建 Account 实体。
3.1.1将光标移动至12行 Account 报错的位置, 使用快捷键:option+enter(mac)/ alt + enter(windows)
。
3.1.2选择第一个 Create class ‘Account’,在弹出的界面中编辑 Account 实体类的路径,选择代码仓库位置,点击 ok / 点击键盘的 enter 键。
这时Account类就创建完成了,并且测试类 AccountServiceTest 中会自动完成包引用。
3.1.3 在 Account 类中添加用户姓名字段,以及 @Builder 注解(lombok)。
@Builder
public class Account {
private String name;
}
此时再回到测试类中,刚才关于 Account 类的编译错误已经解决。
其中从 Account 类快速回到测试类 AccountServiceTest 的快捷键:option + command + ← (mac) / alt + control + ← (windows)
。
3.2我们用同样的方式创建接口类 AccountService。
3.2.1将光标移动至7行 AccountService 报错的位置,使用快捷键:option+enter(mac)/alt + enter(windows)
。
3.2.2选择第一个 Create interface ‘AccountService’,在弹出的界面中编辑 AccountService 接口类的路径,选择代码仓库位置,点击 ok / 点击键盘的 enter 键。
再次切回测试类 AccountServiceTest 中,接口 AccountService 的编译错误已经解决。
3.2.3 将光标移动 19 行的 add 方法位置,使用快捷键:option+enter(mac)/ alt + enter(windows)
,选择 Create method ‘add’ in ‘AccountService’,快速创建 add 方法。
由于我们 add 方法的返回值是返回保存的行数,所以将返回参数类型改为 Integer。
修改完成后,AccountService 接口类的代码为:
public interface AccountService {
Integer add(Account account);
}
3.2.4再次切回 AccountServiceTest 类,19 行 add 方法位置的编译错误已经解决(option + command + ← (mac) / alt + control + ← (windows)
)。
3.2.5 将光标移动到 8 行的 AccountServiceImpl 编译错误的位置,通过快捷键快速创建实现类,选择 Create class ‘AccountServiceImpl’。
快捷键是不是已经掌握了~ option+enter(mac)/ alt + enter(windows)
。
3.2.6 在弹出的界面中编辑 AccountService 接口类的路径,选择代码仓库位置,点击 ok / 点击键盘的 enter 键。
得到 AccountServiceImpl 类的代码:
public class AccountServiceImpl implements AccountService {
}
但此时因为没有实现 add 方法,所以ide提示报错。
3.2.7 将光标移动到报错的第 5 行 AccountServiceImpl 位置,使用快捷键option+enter(mac)/ alt + enter(windows)
选择 Implement mothods 快速增加 add 的方法实现。
ps: 这里再介绍一个快捷键,可以快速在报错的位置切换 F2
。
3.2.8 由于我们本次只添加 add 方法,所以点击确定,完成方法添加。
得到代码:
public class AccountServiceImpl implements AccountService {
@Override
public Integer add(Account account) {
return null;
}
}
3.2.9 切回 AccountServiceTest 类,AccountServiceImpl 的编译错误也已经解决。
3.2.10 将光标移动到 20 行 assertThatThrownBy 报错的问题,通过快捷键option+enter(mac)/ alt + enter(windows)
,选择 Import static method,引入方法的包。
ps: 可使用 F2
快捷键,快速跳至 20 行。
3.2.11选择导入方法:Assertions.assertThatThrownBy。
自此,shouldAddSuccess 方法的编译错误均已解决。
3.2.12 运行 shouldAddSuccess 方法,得到了我们想要的“运行错误的测试用例”。
ps: 可以使用快捷键 control + shift + R
(光标需要在 shouldAddSuccess 方法中)。
自此我们完成了第一个测试用例中的 红 的部分。
- 使用最简单的实现,让测试用例通过。
4.1 进入 AccountServiceImpl 类。
ps : 这里可以使用快捷键command + E(mac) / contrl + E (windows)
展示最近使用的类,选择需要的 AccountServiceImpl 迅速进入。
4.2 使用最简单的代码让测试用例通过。
@Override
public Integer add(Account account) {
if (null == account.getName()) {
throw new RuntimeException();
}
return null;
}
其中为了获取 name 属性,在 Account 类中增加了@Value 的注解(lombok),所以 Account 类的代码如下:
@Builder
@Value
public class Account {
private String name;
}
4.3 运行测试方法 shouldAddSuccess,测试通过。
ps: 由于我们上一个运行的方法就是 shouldAddSuccess ,所以我们在写完代码后,可以通过快捷键 control + R
直接运行。
自此,我们完成了第一个TDD循环中的 绿 的步骤。
- 由于目前的代码不需要重构,所以我们在此处,跳过第一个 TDD 循环中的 蓝 的部分。
完成第二个测试用例的红绿蓝循环
- 测试用例:使用用户姓名不为空的实体添加用户,保存成功,返回保存数量1
- 实现:
- 编写测试用例
1.1 进入 AccountServiceTest 类,使用快捷键command + N (mac) / control + N (windows)
快速创建一个测试方法 shouldAddSuccessWithName 。
得到代码:
@Test
public void shouldAddSuccessWithName() {
}
1.2 完成单元测试 shouldAddSuccessWithName。
@Test
public void shouldAddSuccessWithName() {
//构造一个用户姓名为 aoge 的用户实体 : account
Account account = Account
.builder()
.name("aoge")
.build();
//通过add方法成功添加用户:account,返回保存成功条数: assertRow
Integer assertRow = accountService.add(account);
//判断添加成功条数 assertRow 是否为 1
assertThat(assertRow).isOne();
}
- 使测试用例可以运行失败
由于没有编译错误,所以直接运行方法 shouldAddSuccessWithName。
自此,完成第二个测试用例的 红 的部分
- 最简单的代码使测试用例通过
3.1 修改 add 方法的返回值,代码:
@Override
public Integer add(Account account) {
if (null == account.getName()) {
throw new RuntimeException();
}
return 1;
}
3.2 运行 shouldAddSuccessWithName 方法。
ps : 可以直接使用快捷键 control + R / control + R
运行。
自此完成第二个测试用例的 绿 的部分
- 由于目前代码暂时也没有需要重构的地方,所以跳过 蓝 的部分
自此完成第二个测试用例的循环
end~