编写干净的测试-被认为有害的新内容

很难为干净的代码找到一个好的定义,因为我们每个人都有自己的单词clean的定义。 但是,有一个似乎是通用的定义:

干净的代码易于阅读。

这可能会让您感到有些惊讶,但是我认为该定义也适用于测试代码。 使测试尽可能具有可读性是我们的最大利益,因为:

  • 如果我们的测试易于阅读,那么很容易理解我们的代码是如何工作的。
  • 如果我们的测试易于阅读,那么如果测试失败(不使用调试器),很容易发现问题。

编写干净的测试并不难,但是需要大量的实践,这就是为什么如此多的开发人员为此苦苦挣扎的原因。

我也为此感到挣扎,这就是为什么我决定与您分享我的发现的原因。

这是本教程的第四部分,描述了我们如何编写干净的测试。 这次我们将学习为什么不使用new关键字在测试方法中创建对象的原因。 我们还将学习如何用工厂方法和测试数据构建器替换new关键字。

新不是新黑

在本教程中,我们一直在重构单元测试,以确保当使用唯一的电子邮件地址和社交登录提供者创建新用户帐户时, RepositoryUserService类的registerNewUserAccount(RegistrationForm userAccountData)方法能够按预期工作。

RegistrationForm类是一个数据传输对象(DTO) ,我们的单元测试使用setter方法设置其属性值。 我们的单元测试的源代码如下所示(相关代码突出显示):

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.security.crypto.password.PasswordEncoder;import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class)
public class RepositoryUserServiceTest {private static final String REGISTRATION_EMAIL_ADDRESS = "john.smith@gmail.com";private static final String REGISTRATION_FIRST_NAME = "John";private static final String REGISTRATION_LAST_NAME = "Smith";private static final Role ROLE_REGISTERED_USER = Role.ROLE_USER;private static final SocialMediaService SOCIAL_SIGN_IN_PROVIDER = SocialMediaService.TWITTER;private RepositoryUserService registrationService;@Mockprivate PasswordEncoder passwordEncoder;@Mockprivate UserRepository repository;@Beforepublic void setUp() {registrationService = new RepositoryUserService(passwordEncoder, repository);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldCreateNewUserAccountAndSetSignInProvider() throws DuplicateEmailException       {RegistrationForm registration = new RegistrationForm();registration.setEmail(REGISTRATION_EMAIL_ADDRESS);registration.setFirstName(REGISTRATION_FIRST_NAME);registration.setLastName(REGISTRATION_LAST_NAME);registration.setSignInProvider(SOCIAL_SIGN_IN_PROVIDER);when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);when(repository.save(isA(User.class))).thenAnswer(new Answer<User>() {@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {Object[] arguments = invocation.getArguments();return (User) arguments[0];}});User createdUserAccount = registrationService.registerNewUserAccount(registration);assertEquals(REGISTRATION_EMAIL_ADDRESS, createdUserAccount.getEmail());assertEquals(REGISTRATION_FIRST_NAME, createdUserAccount.getFirstName());assertEquals(REGISTRATION_LAST_NAME, createdUserAccount.getLastName());assertEquals(SOCIAL_SIGN_IN_PROVIDER, createdUserAccount.getSignInProvider());assertEquals(ROLE_REGISTERED_USER, createdUserAccount.getRole());assertNull(createdUserAccount.getPassword());verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);verify(repository, times(1)).save(createdUserAccount);verifyNoMoreInteractions(repository);verifyZeroInteractions(passwordEncoder);}
}

那么,有什么问题呢? 我们的单元测试中突出显示的部分很短,而且相对容易阅读。 我认为,此代码的最大问题是它是以数据为中心的。 它创建了一个新的RegistrationForm对象并设置了创建对象的属性值,但是没有描述这些属性值的含义。

如果我们使用new关键字在测试方法中创建新对象,则由于以下原因,我们的测试将变得更难以阅读:

  1. 读者必须知道所创建对象的不同状态。 例如,如果我们考虑示例,读者必须知道如果我们创建一个新的RegistrationForm对象并设置emailfirstNamelastNamesignInProvider属性的属性值,则意味着该对象是一个注册,即通过使用社交登录提供商进行。
  2. 如果创建的对象具有许多属性,则创建该对象的代码会乱码我们测试的源代码。 我们应该记住,即使我们在测试中需要这些对象,我们也应该集中精力描述被测试方法/功能的行为。

尽管不能完全消除这些缺点是不现实的,但我们应尽最大努力将其影响降到最低,并使我们的测试尽可能易于阅读。

让我们找出如何使用工厂方法来做到这一点。

使用工厂方法

当我们使用工厂方法创建新对象时,我们应该以这种方式命名工厂方法及其方法参数,以使我们的代码更易于读写。 让我们看一下两种不同的工厂方法,看看它们对单元测试的可读性有什么样的影响。

这些工厂方法通常添加到对象母类中,因为它们通常对多个测试类有用。 但是,由于我想保持简单,因此将它们直接添加到测试类中。

第一个工厂方法的名称是newRegistrationViaSocialSignIn() ,并且没有方法参数。 在将此工厂方法添加到测试类之后,单元测试的源如下所示(相关部分已突出显示):

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.security.crypto.password.PasswordEncoder;import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class)
public class RepositoryUserServiceTest {private static final String REGISTRATION_EMAIL_ADDRESS = "john.smith@gmail.com";private static final String REGISTRATION_FIRST_NAME = "John";private static final String REGISTRATION_LAST_NAME = "Smith";private static final Role ROLE_REGISTERED_USER = Role.ROLE_USER;private static final SocialMediaService SOCIAL_SIGN_IN_PROVIDER = SocialMediaService.TWITTER;private RepositoryUserService registrationService;@Mockprivate PasswordEncoder passwordEncoder;@Mockprivate UserRepository repository;@Beforepublic void setUp() {registrationService = new RepositoryUserService(passwordEncoder, repository);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldCreateNewUserAccountAndSetSignInProvider() throws DuplicateEmailException {RegistrationForm registration = newRegistrationViaSocialSignIn();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);when(repository.save(isA(User.class))).thenAnswer(new Answer<User>() {@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {Object[] arguments = invocation.getArguments();return (User) arguments[0];}});User createdUserAccount = registrationService.registerNewUserAccount(registration);assertEquals(REGISTRATION_EMAIL_ADDRESS, createdUserAccount.getEmail());assertEquals(REGISTRATION_FIRST_NAME, createdUserAccount.getFirstName());assertEquals(REGISTRATION_LAST_NAME, createdUserAccount.getLastName());assertEquals(SOCIAL_SIGN_IN_PROVIDER, createdUserAccount.getSignInProvider());assertEquals(ROLE_REGISTERED_USER, createdUserAccount.getRole());assertNull(createdUserAccount.getPassword());verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);verify(repository, times(1)).save(createdUserAccount);verifyNoMoreInteractions(repository);verifyZeroInteractions(passwordEncoder);}private RegistrationForm newRegistrationViaSocialSignIn() {RegistrationForm registration = new RegistrationForm();registration.setEmail(REGISTRATION_EMAIL_ADDRESS);registration.setFirstName(REGISTRATION_FIRST_NAME);registration.setLastName(REGISTRATION_LAST_NAME);registration.setSignInProvider(SOCIAL_SIGN_IN_PROVIDER);return registration;}
}

第一种工厂方法具有以下后果:

  • 测试方法的一部分,它创建了新的RegistrationForm对象,比以前干净得多,并且工厂方法的名称描述了创建的RegistrationForm对象的状态。
  • 我们的模拟对象的配置更难以阅读,因为email属性的值在我们的工厂方法中被“隐藏”了。
  • 因为创建的RegistrationForm对象的属性值被“隐藏”在我们的工厂方法中,所以我们的断言更加难以理解。

如果我们使用对象母模式 ,则问题会更大,因为我们必须将相关的常量移至对象母类。

我认为可以说,尽管第一种工厂方法有其好处,但它也有严重的缺点。

让我们看看第二种工厂方法是否可以消除这些缺点。

第二个工厂方法的名称为newRegistrationViaSocialSignIn() ,并且它将电子邮件地址,名字,姓氏和提供程序中的社交符号作为方法参数。 在将此工厂方法添加到测试类之后,单元测试的源如下所示(相关部分已突出显示):

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.security.crypto.password.PasswordEncoder;import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class)
public class RepositoryUserServiceTest {private static final String REGISTRATION_EMAIL_ADDRESS = "john.smith@gmail.com";private static final String REGISTRATION_FIRST_NAME = "John";private static final String REGISTRATION_LAST_NAME = "Smith";private static final Role ROLE_REGISTERED_USER = Role.ROLE_USER;private static final SocialMediaService SOCIAL_SIGN_IN_PROVIDER = SocialMediaService.TWITTER;private RepositoryUserService registrationService;@Mockprivate PasswordEncoder passwordEncoder;@Mockprivate UserRepository repository;@Beforepublic void setUp() {registrationService = new RepositoryUserService(passwordEncoder, repository);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldCreateNewUserAccountAndSetSignInProvider() throws DuplicateEmailException {RegistrationForm registration = newRegistrationViaSocialSignIn(REGISTRATION_EMAIL_ADDRESS,REGISTRATION_FIRST_NAME,REGISTRATION_LAST_NAME,SOCIAL_MEDIA_SERVICE);when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);when(repository.save(isA(User.class))).thenAnswer(new Answer<User>() {@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {Object[] arguments = invocation.getArguments();return (User) arguments[0];}});User createdUserAccount = registrationService.registerNewUserAccount(registration);assertEquals(REGISTRATION_EMAIL_ADDRESS, createdUserAccount.getEmail());assertEquals(REGISTRATION_FIRST_NAME, createdUserAccount.getFirstName());assertEquals(REGISTRATION_LAST_NAME, createdUserAccount.getLastName());assertEquals(SOCIAL_SIGN_IN_PROVIDER, createdUserAccount.getSignInProvider());assertEquals(ROLE_REGISTERED_USER, createdUserAccount.getRole());assertNull(createdUserAccount.getPassword());verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);verify(repository, times(1)).save(createdUserAccount);verifyNoMoreInteractions(repository);verifyZeroInteractions(passwordEncoder);}private RegistrationForm newRegistrationViaSocialSignIn(String emailAddress, String firstName, String lastName, SocialMediaService signInProvider) {RegistrationForm registration = new RegistrationForm();registration.setEmail(emailAddress);registration.setFirstName(firstName);registration.setLastName(lastName);registration.setSignInProvider(signInProvider);return registration;}
}

第二种工厂方法具有以下后果:

  • 我们的测试方法的一部分(创建新的RegistrationForm对象)比使用第一个工厂方法的相同代码稍微有些混乱。 但是,它仍然比原始代码干净,因为factory方法的名称描述了创建对象的状态。
  • 似乎消除了第一个工厂方法的弊端,因为创建的对象的属性值未“隐藏”在工厂方法内部。

看起来很酷,对吧?

真的很容易想到天堂里一切都很好,但是事实并非如此。 尽管我们已经看到工厂方法可以使我们的测试更具可读性,但事实是,只有满足以下条件,它们才是一个不错的选择:

  1. 工厂方法没有太多的方法参数。 当方法参数的数量增加时,我们的测试将变得更加难以编写和读取。 显而易见的问题是:工厂方法可以有多少个方法参数? 不幸的是,很难给出确切的答案,但是我认为,如果工厂方法只有少数方法参数,那么使用工厂方法是一个不错的选择。
  2. 测试数据没有太大的差异。 使用工厂方法的问题是单个工厂方法通常适用于一个用例。 如果我们需要支持N个用例,则需要N种工厂方法。 这是一个问题,因为随着时间的流逝,我们的工厂方法变得ated肿,混乱并且难以维护(尤其是如果使用对象母模式)。

让我们找出测试数据生成器是否可以解决其中一些问题。

使用测试数据构建器

测试数据构建器是使用构建器模式创建新对象的类。 Effective Java中描述的构建器模式有很多好处 ,但是我们的主要动机是提供一个流畅的API以创建测试中使用的对象。

我们可以按照以下步骤创建一个测试数据构建器类,该类创建新的RegistrationForm对象:

  1. 创建一个RegistrationFormBuilder类。
  2. RegistrationForm字段添加到创建的类。 该字段包含对创建对象的引用。
  3. 向创建的类中添加默认构造函数,并通过创建新的RegistrationForm对象来实现它。
  4. 添加用于设置创建的RegistrationForm对象的属性值的方法。 每个方法都通过调用正确的setter方法来设置属性值,并返回对RegistrationFormBuilder对象的引用。 请记住,这些方法的方法名称可以建立或破坏我们的DSL
  5. 向所创建的类中添加一个build()方法,并通过返回所创建的RegistrationForm对象来实现它。

我们的测试数据构建器类的源代码如下所示:

public class RegistrationFormBuilder {private RegistrationForm registration;public RegistrationFormBuilder() {registration = new RegistrationForm();}public RegistrationFormBuilder email(String email) {registration.setEmail(email);return this;}public RegistrationFormBuilder firstName(String firstName) {registration.setFirstName(firstName);return this;}public RegistrationFormBuilder lastName(String lastName) {registration.setLastName(lastName);return this;}public RegistrationFormBuilder isSocialSignInViaSignInProvider(SocialMediaService signInProvider) {registration.setSignInProvider(signInProvider);return this;}public RegistrationForm build() {return registration;}
}

在修改单元测试以使用新的测试数据构建器类之后,其源代码如下所示(相关部分已突出显示):

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.security.crypto.password.PasswordEncoder;import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;@RunWith(MockitoJUnitRunner.class)
public class RepositoryUserServiceTest {private static final String REGISTRATION_EMAIL_ADDRESS = "john.smith@gmail.com";private static final String REGISTRATION_FIRST_NAME = "John";private static final String REGISTRATION_LAST_NAME = "Smith";private static final Role ROLE_REGISTERED_USER = Role.ROLE_USER;private static final SocialMediaService SOCIAL_SIGN_IN_PROVIDER = SocialMediaService.TWITTER;private RepositoryUserService registrationService;@Mockprivate PasswordEncoder passwordEncoder;@Mockprivate UserRepository repository;@Beforepublic void setUp() {registrationService = new RepositoryUserService(passwordEncoder, repository);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldCreateNewUserAccountAndSetSignInProvider() throws DuplicateEmailException {RegistrationForm registration = new RegistrationFormBuilder().email(REGISTRATION_EMAIL_ADDRESS).firstName(REGISTRATION_FIRST_NAME).lastName(REGISTRATION_LAST_NAME).isSocialSignInViaSignInProvider(SOCIAL_SIGN_IN_PROVIDER).build();when(repository.findByEmail(REGISTRATION_EMAIL_ADDRESS)).thenReturn(null);when(repository.save(isA(User.class))).thenAnswer(new Answer<User>() {@Overridepublic User answer(InvocationOnMock invocation) throws Throwable {Object[] arguments = invocation.getArguments();return (User) arguments[0];}});User createdUserAccount = registrationService.registerNewUserAccount(registration);assertEquals(REGISTRATION_EMAIL_ADDRESS, createdUserAccount.getEmail());assertEquals(REGISTRATION_FIRST_NAME, createdUserAccount.getFirstName());assertEquals(REGISTRATION_LAST_NAME, createdUserAccount.getLastName());assertEquals(SOCIAL_SIGN_IN_PROVIDER, createdUserAccount.getSignInProvider());assertEquals(ROLE_REGISTERED_USER, createdUserAccount.getRole());assertNull(createdUserAccount.getPassword());verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);verify(repository, times(1)).save(createdUserAccount);verifyNoMoreInteractions(repository);verifyZeroInteractions(passwordEncoder);}
}

如我们所见,测试数据构建器具有以下优点:

  • 创建新的RegistrationForm对象的代码易于阅读和编写。 我非常喜欢流畅的API,并且我认为这段代码既优美又优雅。
  • 构建器模式确保从我们的测试数据中发现的变化不再是问题,因为我们可以简单地向测试数据构建器类添加新方法。
  • 模拟对象和断言的配置易于阅读,因为常量在我们的测试方法中可见,并且DSL强调每个属性值的含义。

那么,我们应该对所有内容使用构建器模式吗?

没有!

仅在有意义时,才应使用测试数据构建器。 换句话说,我们应该在以下情况下使用它们:

  1. 我们设置了许多属性值。
  2. 我们的测试数据有很大的差异。

如果满足以下条件之一,则构建器模式是一个理想的选择。 原因是我们可以通过命名builder类的setter-like方法来创建特定于域的语言 。 即使我们将创建许多不同的对象并设置许多属性值,这也使我们的测试易于读写。

那是建造者木匠的力量。

如果您想了解有关流利API的更多信息,则应阅读以下文章:

  • 流利的界面
  • Java Fluent API设计器速成课程
  • 用Java构建流畅的API(内部DSL)

今天就这些。 让我们继续并总结从这篇博客文章中学到的知识。

摘要

我们了解了为什么使用new关键字在测试方法中创建对象不是一个好主意,并且我们学习了两种不同的方法来创建在测试中使用的对象。

更具体地说,此博客文章教会了我们三件事:

  • 通过使用new关键字在测试方法中创建所需的对象是一个坏主意,因为它会使我们的测试混乱且难以阅读。
  • 如果我们只需要设置少数几个属性值,而我们的测试数据没有太多变化,则应该使用工厂方法来创建所需的对象。
  • 如果必须设置许多属性值和/或我们的测试数据有很多差异,则应该使用测试数据生成器来创建所需的对象。

翻译自: https://www.javacodegeeks.com/2014/05/writing-clean-tests-new-considered-harmful.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363211.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

所谓经济现象

随想一下&#xff0c;先举出一些基本认知&#xff0c;后面大家会看到这些认知之间的牵扯&#xff0c;已经人在做决策的时候是如何分配权重的&#xff0c;进而产生后续和当今的局面。经济学一点都不难&#xff0c;只是现在人学的是技术上的操作&#xff0c;而不学背后的运作逻辑…

hdu-1277--字典树坑题

hdu-1227 字典树&#xff0c;坑题&#xff01;&#xff01;当字典树练手 Problem Description 我们大家经常用google检索信息&#xff0c;但是检索信息的程序是很困难编写的&#xff1b;现在请你编写一个简单的全文检索程序。 问题的描述是这样的&#xff1a;给定一个信息流文件…

Do not mutate vuex store state outside mutation handlers.

组件代码&#xff1a; selectItem(item,index) {this.selectPlay({list: this.songs,index}) }, ...mapActions([selectPlay ]) mutation 代码&#xff1a; [types.SET_PLAYLIST](state, list) {// 1、state.playlist JSON.parse(JSON.stringify(list))// 2、state.playlist …

硅谷企業面臨新的反壟斷枷鎖

硅谷的公司正面臨著加強反壟斷審查的新階段﹐這是對奧巴馬政府加強執法和持續不斷的海外壓力所做出的反應。 對在其行業中佔據主導地位的企業採取更嚴格的立場可能會考驗這些科技業巨頭在布什政府時期採取的政府關係策略。Associated Press奧巴馬政府任命的司法部負責反壟斷執法…

Spring Java配置:会话超时

当您可以使用基于Java的配置开发Spring应用程序时&#xff0c;我们生活在一个美好的时光。 不再有多余的XML代码&#xff0c;只有纯Java代码。 在本文中&#xff0c;我想讨论一个关于Spring应用程序中会话管理的热门话题。 更确切地说&#xff0c;我将以Java配置样式讨论会话超…

怎样去掉警告 log4j:WARN No appenders could be found for logger

最近在作项目的时候&#xff0c;用到了 HttpClient&#xff0c;用它向 HTTP server 发送请求并处理返回的页面数据。 我的应用类间接调用 HttpClient。在运行的时候&#xff0c;程序打印出了如下警告信息&#xff1a; log4j:WARN No appenders could be found for logger log…

这首歌【好听】到哭出来

转载于:https://www.cnblogs.com/Agnel-Cynthia/p/10662886.html

Raect Router 4 的使用 (1)

本文来自于官方文档&#xff0c;属于意译而非直译 基本组件 React Router 有三种类型的组件&#xff0c;分别是&#xff1a;react-router、react-router-dom、react-router-native 你在web 程序中使用了路由组件&#xff0c;那你就应该引入 react-router-dom&#xff1a; im…

分而治之思想

当一个问题的规模很大时&#xff0c;直接求解往往比较困难。对于这类问题&#xff0c;很大一部分是可以采取分而治之的思想来处理的。 分治法是把问题划分成多个子问题来进行处理。这些子问题&#xff0c;在结构上跟原来的问题一样&#xff0c;但是规模比原来的问题要小。如果得…

在Java 8中使用Stream API解析文件

Java 8中到处都有流。只需四处看看&#xff0c;可以肯定地找到它们。 它也适用于java.io.BufferedReader 。 使用Stream API在Java 8中解析文件非常容易。 我有一个要读取的CSV文件。 下面的例子&#xff1a; username;visited jdoe;10 kolorobot;4我的阅读器的一项合同是提供…

诡异的DateTime.TryParseExact方法

老赵在介绍Routing扩展的WebCast中出了点“小状况”&#xff0c;即将DateTime.ToString(“yyyy-MM-dd”)修改为DateTime.ToString(“yyyy/MM/dd”)后&#xff0c;页面中仍然显示为yyyy-MM-dd样式的日期格式。相信看过WebCast的同学都还记得吧。我不解&#xff0c;将老赵代码中D…

【手撸一个ORM】第一步、实体约定和描述

一、约定 数据实体必须实现 IEntity 接口&#xff0c;该接口定义了一个int类型的Id属性&#xff0c;既每个实体必须有一个名称为Id的自增主键。 若数据表的主键列名称不是Id&#xff0c;可以通过 [MyKey("主键列名")] 对该属性进行描述 namespace MyOrm.Commons {pub…

this指向问题(2)

4、显示绑定 指的是apply、bind、call &#xff08;1&#xff09;、apply 和 call 相同点&#xff1a; <1> 这两个方法的用途是在特定的作用域中调用函数&#xff0c;实际上等于设置函数体内 this 对象的值&#xff0c;真正强大之处在于扩充函数赖以运行的作用域 <2&g…

将要被淘汰的8种人

将要被淘汰的8种人 从朋友的空间你发现了这篇文章&#xff0c;觉得写的很有道理&#xff0c;把它转过来&#xff0c;以便时时提醒一下自己&#xff1a; 不景气的社会&#xff0c;一定会淘汰不争气的人。如何不被淘汰&#xff0c;只要你不在这八种人之内。如果你在其中之内&…

学习 AngularJs 终于有点进步了。

很庆幸&#xff0c;自己在7月股市最低点的时候上车了&#xff0c;&#xff0c;开始了在股市的第一票&#xff0c;现在还不知道会不会赚&#xff0c;虽然买的核电有点偏高&#xff0c;但是还是对国家大工业大工程有信心的&#xff0c;按照今天的走势应该赚不少&#xff0c;希望是…

泽西岛2.9及更高版本中的声明式链接

几个星期前几个月前&#xff0c;我正在寻找如何为Oracle Cloud项目设计新的REST API。 我计划要做的事情之一就是使用Marc Hadley在Jersey 1.x中创建的声明性链接注入。 可悲的是这并没有被转发移植然而&#xff0c;这样一个快速的聊天项目带动和我承担了使代码最新的小中型工作…

使用HTML5技术控制电脑或手机上的摄像头

移动设备和桌面电脑上的客户端API起初并不是同步的。最初总是移动设备上先拥有某些功能和相应的API&#xff0c;但慢慢的&#xff0c;这些API会出现在桌面电脑上。其中一个应用接口技术就是getUserMedia API&#xff0c;它能让应用开发者访问用户的摄像头或内置相机。下面就展示…

好的,每个接触Java字节码的人

Oracle诉Google一案认为&#xff0c;复制Java API的结构&#xff0c;序列和组织是侵犯版权的行为。 侵犯版权不仅是复制行为&#xff0c;而且还适用于拥有该作品副本的所有中间方。 那就是编写/编译任何JVM语言的人&#xff0c;以及在他们拥有的任何设备上都有JAR文件的人&…

手动安装boost库

手动安装boost库。 1,下载boost库&#xff0c;解压到目录。我解压的目录为D:\thirdParty。2&#xff0c;build bjam工具。 转到 D:\thirdParty\boost_1_37_0\tools\jam\src目录&#xff0c;执行build.bat即可生成bin.ntx86目录&#xff0c;里面有个bjam.exe&#xff0c;以后bui…

iSlide——图标库、图示库的用法

iSlide中&#xff0c;有一个“图示库”功能&#xff0c;主要功能是同时排列多块文字或多张图片。单击插图库&#xff0c;会弹出一个新的对话框。从中&#xff0c;可以选择权限、分类、数量数据和样式&#xff0c;也可以直接搜索。 下面就举一个例子&#xff1a;我要开一个班队会…