900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 近似线性依靠matlab_不要仅仅依靠单元测试

近似线性依靠matlab_不要仅仅依靠单元测试

时间:2021-08-07 09:54:27

相关推荐

近似线性依靠matlab_不要仅仅依靠单元测试

近似线性依靠matlab

当您构建一个复杂的系统时,仅仅测试组件是不够的。 这很关键,但还不够。 想象一下一家汽车厂生产和进口最高质量的零件,但组装好汽车后再也没有启动发动机。 如果您的测试用例套件几乎不包含单元测试,那么您将永远无法确保系统整体正常运行。 让我们举一个人为的例子:

public class UserDao {public List<User> findRecentUsers() {try {return //run some query} catch(EmptyResultDataAccessException ignored) {return null;}}//...}

我希望您已经在catch块中发现了一个反模式(而且我并不是想忽略异常,这似乎是可以预期的)。 作为好公民,我们决定通过以下方式解决问题: 返回一个空集合,而不是null

public class UserDao {public List<User> findRecentUsers() {try {return //run some query} catch(EmptyResultDataAccessException ignored) {return Collections.emptyList();}}//...}

修复非常简单,我们几乎忘记了运行单元测试,但是以防万一我们执行它们并发现第一个失败的测试用例:

public class UserDaoTest {private UserDao userDao;@Beforepublic void setUp() throws Exception {userDao = new UserDao();}@Testpublic void shouldReturnNullWhenNoRecentUsers() throws Exception {//given//whenfinal List<User> result = userDao.findRecentUsers();//thenassertThat(result).isNull();}@Testpublic void shouldReturnOneRecentUser() throws Exception {//givenfinal User lastUser = new User();userDao.storeLoginEvent(lastUser);//whenfinal List<User> result = userDao.findRecentUsers();//thenassertThat(result).containsExactly(lastUser);}@Testpublic void shouldReturnTwoRecentUsers() throws Exception {//givenfinal User lastUser = new User();final User oneButLastUser = new User();userDao.storeLoginEvent(oneButLastUser);userDao.storeLoginEvent(lastUser);//whenfinal List<User> result = userDao.findRecentUsers();//thenassertThat(result).containsExactly(lastUser, oneButLastUser);}}

显然,不仅代码被破坏了(通过返回null而不是像null的空集合),而且还有一个测试来验证这种虚假行为。 我很确定测试是在实现之后编写的,并且必须以某种方式处理现实。 如果没有对实现特性的事先了解,就不会有人编写这样的测试。 因此,我们修复了测试,并乐意等待绿色CI的建立–最终来了。 几天后,我们的应用程序在生产中因NullPointerException中断。 它打破了经过彻底的单元测试的地方:

public class StatService {private final UserDao userDao;public StatService(UserDao userDao) {this.userDao = userDao;}public void welcomeMostRecentUser() {final List<User> recentUsers = userDao.findRecentUsers();if (recentUsers != null) {welcome(recentUsers.get(0));}}private void welcome(User user) {//...}}

我们很惊讶,因为此类已被单元测试完全覆盖(为清楚起见,省略了验证步骤):

@RunWith(MockitoJUnitRunner.class)public class WelcomeServiceTest {@Mockprivate UserDao userDaoMock;private WelcomeService welcomeService;@Beforepublic void setup() {welcomeService = new WelcomeService(userDaoMock);}@Testpublic void shouldNotSendWelcomeMessageIfNoRecentUsers() throws Exception {//givengiven(userDaoMock.findRecentUsers()).willReturn(null);//whenwelcomeService.welcomeMostRecentUser();//then//verify no message sent}@Testpublic void shouldSendWelcomeMessageToMostRecentUser() throws Exception {//givengiven(userDaoMock.findRecentUsers()).willReturn(asList(new User()));//whenwelcomeService.welcomeMostRecentUser();//then//verify user welcomed}//...}

您知道问题出在哪里吗? 我们更改了UserDao类的合同,同时使它在表面上“看起来”相同。 通过修复损坏的测试,我们认为它仍然可以工作。 但是,WelcomeService仍然依赖UserDao的旧行为,该行为要么返回null,要么返回具有至少一个元素的列表。 使用模拟框架记录了此行为,因此我们能够对单元中的WelcomeService进行单独测试。 换句话说,我们无法确保这两个组件仍然可以正常工作,我们仅对它们进行了单独测试。 回到我们的汽车隐喻–所有部件仍然可以放在一起(相同的合同),但是其中一个部件的内部行为与以前不同。 那么,到底出了什么问题? 这里至少有四个问题,如果解决了其中任何一个,这些都不会发生。

首先,UserDao的作者未能认识到返回null而空列表似乎更直观。 这就引出了一个问题:null和empty集合之间有显着区别吗? 如果是,也许您正在尝试在单个返回值中“编码”太多信息? 如果没有,为什么还要增加API使用者的生活呢? 遍历空集合不需要任何额外的工作; 对可能为nullcollection进行迭代需要一个额外的条件。WelcomeService作者也因为假定null表示一个空集合而失败。 他应该处理丑陋的API,而不是依赖它。 在这种情况下,他本可以使用CollectionUtils.isNotEmpty()并采取一些防御措施:

if (CollectionUtils.isNotEmpty(recentUsers)) {

对于更全面的解决方案,他还可以考虑装饰UserDao并将null替换为空collection。 甚至使用AOP在整个应用程序中全局修复此类API。 顺便说一句,这也适用于String。 在99%的情况下,null,空字符串和很少有空格的字符串之间没有“业务”差异。 除非您真的想区分它们,否则默认情况下使用StringUtils.isBlank()或类似名称。

最终,“修复”UserDao的人看不到大图。 仅仅修复单元测试是不够的。 当您在不更改API的情况下更改类的行为时(这对于动态语言尤为重要),您很可能会错过使用该API的地方,从而失去上下文。 但是最大的失败是缺少组件/系统测试。 如果我们只是简单地测试了一个同时运行WelcomeServiceUserDao,那么就会发现此错误。 仅有100%的代码覆盖率是不够的。 您测试了拼图游戏的每一个片段,但从未看过完成的图片。 至少进行一些较大的烟雾测试。 否则,您将不再具有如此强大的信心,即当测试呈绿色时,代码就可以使用了。

参考:不要仅仅依靠我们的JCG合作伙伴 Tomasz Nurkiewicz的NoBlogDefFound博客中的单元测试 。

翻译自: //02/dont-rely-on-unit-tests-alone.html

近似线性依靠matlab

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。