验证码编写_编写干净的测试–验证或不验证

验证码编写

在编写使用模拟对象的单元测试时,请遵循以下步骤:

  1. 配置我们的模拟对象的行为。
  2. 调用测试的方法。
  3. 验证是否已调用模拟对象的正确方法。

第三步的描述实际上有点误导,因为通常我们最终会验证是否调用了正确的方法以及未调用模拟对象的其他方法。

每个人都知道,如果我们要编写无错误的软件,我们必须验证这两种情况或不良情况的发生。

对?

让我们验证一切

让我们首先看一下用于向数据库添加新用户帐户的服务方法的实现。

此服务方法的要求是:

  • 如果注册用户帐户的电子邮件地址不是唯一的,我们的服务方法必须抛出异常。
  • 如果注册的用户帐户具有唯一的电子邮件地址,则我们的服务方法必须将新的用户帐户添加到数据库中。
  • 如果注册的用户帐户具有唯一的电子邮件地址,并且是使用常规登录创建的,则我们的服务方法必须先对用户密码进行编码,然后再将其保存到数据库中。
  • 如果注册的用户帐户具有唯一的电子邮件地址,并且是使用社交登录创建的,则我们的服务方法必须保存使用的社交登录提供商。
  • 通过使用社交登录创建的用户帐户不得具有密码。
  • 我们的服务方法必须返回创建的用户帐户的信息。

如果您想了解如何指定服务方法的要求,则应阅读以下博客文章:

  • 从上到下:Web应用程序的TDD
  • 从构思到代码:敏捷规范的生命周期

通过执行以下步骤来实现此服务方法:

  1. 服务方法检查是否从数据库中找不到用户提供的电子邮件地址。 它是通过调用UserRepository接口的findByEmail()方法来实现的。
  2. 如果找到User对象,则服务方法方法将抛出DuplicateEmailException
  3. 它创建一个新的User对象。 如果通过使用常规登录进行注册 (未设置RegistrationForm类的signInProvider属性),则service方法将对用户提供的密码进行编码,并将编码后的密码设置为创建的User对象。
  4. 服务方法将创建的User对象的信息保存到数据库中,并返回保存的User对象。

RepositoryUserService类的源代码如下所示:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class RepositoryUserService implements UserService {private PasswordEncoder passwordEncoder;private UserRepository repository;@Autowiredpublic RepositoryUserService(PasswordEncoder passwordEncoder, UserRepository repository) {this.passwordEncoder = passwordEncoder;this.repository = repository;}@Transactional@Overridepublic User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException {if (emailExist(userAccountData.getEmail())) {throw new DuplicateEmailException("The email address: " + userAccountData.getEmail() + " is already in use.");}String encodedPassword = encodePassword(userAccountData);User registered = User.getBuilder().email(userAccountData.getEmail()).firstName(userAccountData.getFirstName()).lastName(userAccountData.getLastName()).password(encodedPassword).signInProvider(userAccountData.getSignInProvider()).build();return repository.save(registered);}private boolean emailExist(String email) {User user = repository.findByEmail(email);if (user != null) {return true;}return false;}private String encodePassword(RegistrationForm dto) {String encodedPassword = null;if (dto.isNormalRegistration()) {encodedPassword = passwordEncoder.encode(dto.getPassword());}return encodedPassword;}
}

如果我们要编写单元测试以确保当用户通过使用社交登录注册新用户帐户时我们的服务方法能够正常工作,并且我们要验证我们的服务方法与模拟对象之间的每一次交互,我们必须编写八个对其进行单元测试。

我们必须确保:

  • 当提供重复的电子邮件地址时,服务方法将检查电子邮件地址是否唯一。
  • 给定重复的电子邮件地址时,将引发DuplicateEmailException
  • 给定重复的电子邮件地址时,service方法不会将新帐户保存到数据库中。
  • 如果提供重复的电子邮件地址,我们的服务方法不会对用户的密码进行编码。
  • 当提供唯一的电子邮件地址时,我们的服务方法会检查电子邮件地址是否唯一。
  • 当给出唯一的电子邮件地址时,我们的服务方法将创建一个包含正确信息的新User对象,并将创建的User对象的信息保存到数据库中。
  • 当给出唯一的电子邮件地址时,我们的服务方法将返回创建的用户帐户的信息。
  • 当提供唯一的电子邮件地址并使用社交登录名时,我们的服务方法不得设置已创建用户帐户的密码(或对其进行编码)。

我们的测试类的源代码如下所示:

import net.petrikainulainen.spring.social.signinmvc.user.dto.RegistrationForm;
import net.petrikainulainen.spring.social.signinmvc.user.dto.RegistrationFormBuilder;
import net.petrikainulainen.spring.social.signinmvc.user.model.SocialMediaService;
import net.petrikainulainen.spring.social.signinmvc.user.model.User;
import net.petrikainulainen.spring.social.signinmvc.user.repository.UserRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
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 com.googlecode.catchexception.CatchException.catchException;
import static com.googlecode.catchexception.CatchException.caughtException;
import static net.petrikainulainen.spring.social.signinmvc.user.model.UserAssert.assertThatUser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
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 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_SocialSignInAndDuplicateEmail_ShouldCheckThatEmailIsUnique() 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(new User());catchException(registrationService).registerNewUserAccount(registration);verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldThrowException() 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(new User());catchException(registrationService).registerNewUserAccount(registration);assertThat(caughtException()).isExactlyInstanceOf(DuplicateEmailException.class);}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotSaveNewUserAccount() 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(new User());catchException(registrationService).registerNewUserAccount(registration);verify(repository, never()).save(isA(User.class));}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotCreateEncodedPasswordForUser() 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(new User());catchException(registrationService).registerNewUserAccount(registration);verifyZeroInteractions(passwordEncoder);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldCheckThatEmailIsUnique() 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);registrationService.registerNewUserAccount(registration);verify(repository, times(1)).findByEmail(REGISTRATION_EMAIL_ADDRESS);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldSaveNewUserAccountAndSetSignInProvider() 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);registrationService.registerNewUserAccount(registration);ArgumentCaptor<User> userAccountArgument = ArgumentCaptor.forClass(User.class);verify(repository, times(1)).save(userAccountArgument.capture());User createdUserAccount = userAccountArgument.getValue();assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldReturnCreatedUserAccount() 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);assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAnUniqueEmail_ShouldNotCreateEncodedPasswordForUser() 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);registrationService.registerNewUserAccount(registration);verifyZeroInteractions(passwordEncoder);}
}

这些单元测试是按照本教程前面部分中给出的说明编写的。

该课程有很多单元测试。 我们确定他们每个人都是真的必要吗?

或者可能不是

一个明显的问题是,我们编写了两个单元测试,两个单元测试都验证我们的服务方法检查了用户提供的电子邮件地址是否唯一。 我们可以通过将这些测试合并为一个单元测试来解决此问题。 毕竟,一项测试应该使我们确信,我们的服务方法会在创建新用户帐户之前验证用户提供的电子邮件地址是否唯一。

但是,如果这样做,我们将找不到更有趣的问题的答案。 这个问题是:

我们是否应该真的验证测试代码和模拟对象之间的每一次交互?

几个月前,我遇到了James Coplien撰写的标题为: 为什么大多数单元测试都是浪费的文章。 本文提出了几点要点,但其中之一非常适合这种情况。 詹姆斯·科普林(James Coplien)认为,对于测试套件中的每个测试,我们应该提出一个问题:

如果该测试失败,那么将损害哪些业务要求?

他还解释了为什么这是一个如此重要的问题:

在大多数情况下,答案是“我不知道”。 如果您不知道测试的价值,那么从理论上讲,测试的商业价值可能为零。 测试确实要付出代价:维护,计算时间,管理等等。 这意味着测试可能具有净负值。 这是要删除的第四类测试。

让我们找出使用此问题评估单元测试时会发生什么。

弹出问题

当问一个问题时:“如果该测试失败,将危及哪些业务需求?” 关于测试类的每个单元测试,我们得到以下答案:

  • 当提供重复的电子邮件地址时,服务方法将检查电子邮件地址是否唯一。
    • 用户必须具有唯一的电子邮件地址。
  • 给定重复的电子邮件地址时,将引发DuplicateEmailException
    • 用户必须具有唯一的电子邮件地址。
  • 给定重复的电子邮件地址时,service方法不会将新帐户保存到数据库中。
    • 用户必须具有唯一的电子邮件地址。
  • 如果提供重复的电子邮件地址,我们的服务方法不会对用户的密码进行编码。
  • 当提供唯一的电子邮件地址时,我们的服务方法会检查电子邮件地址是否唯一。
    • 用户必须具有唯一的电子邮件地址。
  • 当给出唯一的电子邮件地址时,我们的服务方法将创建一个包含正确信息的新User对象,并将创建的User对象的信息保存到使用的数据库中。
    • 如果注册的用户帐户具有唯一的电子邮件地址,则必须将其保存到数据库中。
  • 当给出唯一的电子邮件地址时,我们的服务方法将返回创建的用户帐户的信息。
    • 我们的服务方法必须返回创建的用户帐户的信息。
  • 当提供唯一的电子邮件地址并使用社交登录名时,我们的服务方法不得设置已创建用户帐户的密码(或对其进行编码)。
    • 使用社交登录创建的用户帐户没有密码。

乍一看,我们的测试类似乎只有一个没有业务价值(或可能有负净值)的单元测试。 此单元测试可确保当用户尝试使用重复的电子邮件地址创建新的用户帐户时,我们的代码与PasswordEncoder模拟之间没有任何交互。

显然,我们必须删除此单元测试,但这不是唯一必须删除的单元测试。

兔子洞比预期的要深

早些时候,我们注意到我们的测试类包含两个单元测试,两个单元测试都可以验证是否调用了UserRepository接口的findByEmail()方法。 当我们仔细查看测试的服务方法的实现时,我们注意到:

  • UserRepository接口的findByEmail()方法返回User对象时,我们的服务方法将引发DuplicateEmailException
  • UserRepository接口的findByEmail()方法返回null时,我们的服务方法将创建一个新的用户帐户。

经过测试的服务方法的相关部分如下所示:

public User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException {if (emailExist(userAccountData.getEmail())) {//If the PersonRepository returns a Person object, an exception is thrown.throw new DuplicateEmailException("The email address: " + userAccountData.getEmail() + " is already in use.");}//If the PersonRepository returns null, the execution of this method continues.
}private boolean emailExist(String email) {User user = repository.findByEmail(email);if (user != null) {return true;}return false;
}

我认为我们应该删除这两个单元测试,原因有二:

  • 只要我们正确配置了PersonRepository模拟,我们就知道它的findByEmail()方法是通过使用正确的method参数调用的。 尽管我们可以将这些测试用例链接到业务需求(用户的电子邮件地址必须是唯一的),但是我们不需要它们来验证该业务需求没有受到损害。
  • 这些单元测试未记录我们服务方法的API。 他们记录了它的实现。 像这样的测试是有害的,因为它们使我们的测试套件杂乱无章,并且使重构变得更加困难。

如果我们不配置模拟对象,它们将返回“ nice”值。
Mockito常见问题解答指出:

为了透明和不引人注目,默认情况下,所有Mockito模拟都返回“ nice”值。 例如:零,假,空集合或空。 请参阅有关存根的javadocs,以查看确切地返回默认值。

这就是为什么我们应该始终配置相关的模拟对象的原因! 如果我们不这样做,我们的测试可能就没有用了。

让我们继续前进,清理这个烂摊子。

清理混乱

从测试类中删除这些单元测试后,其源代码如下所示:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
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 com.googlecode.catchexception.CatchException.catchException;
import static com.googlecode.catchexception.CatchException.caughtException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
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 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_SocialSignInAndDuplicateEmail_ShouldThrowException() 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(new User());catchException(registrationService).registerNewUserAccount(registration);assertThat(caughtException()).isExactlyInstanceOf(DuplicateEmailException.class);}@Testpublic void registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotSaveNewUserAccount() 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(new User());catchException(registrationService).registerNewUserAccount(registration);verify(repository, never()).save(isA(User.class));}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldSaveNewUserAccountAndSetSignInProvider() 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);registrationService.registerNewUserAccount(registration);ArgumentCaptor<User> userAccountArgument = ArgumentCaptor.forClass(User.class);verify(repository, times(1)).save(userAccountArgument.capture());User createdUserAccount = userAccountArgument.getValue();assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldReturnCreatedUserAccount() 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);assertThatUser(createdUserAccount).hasEmail(REGISTRATION_EMAIL_ADDRESS).hasFirstName(REGISTRATION_FIRST_NAME).hasLastName(REGISTRATION_LAST_NAME).isRegisteredUser().isRegisteredByUsingSignInProvider(SOCIAL_SIGN_IN_PROVIDER);}@Testpublic void registerNewUserAccount_SocialSignInAnUniqueEmail_ShouldNotCreateEncodedPasswordForUser() 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);registrationService.registerNewUserAccount(registration);verifyZeroInteractions(passwordEncoder);}
}

我们从测试类中删除了三个单元测试,因此,我们可以享受以下好处:

  • 我们的测试班的单元测试较少 。 这似乎是一个奇怪的好处,因为经常建议我们编写尽可能多的单元测试。 但是,如果考虑到这一点,那么减少单元测试是有意义的,因为我们需要维护的测试较少。 这以及每个单元只能测试一件事的事实使我们的代码更易于维护和重构。
  • 我们提高了文档的质量 。 删除的单元测试未记录测试服务方法的公共API。 他们记录了它的实施。 由于这些测试已删除,因此更容易弄清测试服务方法的要求。

摘要

这篇博客文章教会了我们三件事:

  • 如果我们无法确定单元测试失败的业务需求,则不应该编写该测试。
  • 我们不应该编写没有记录测试方法的公共API的单元测试,因为这些测试使我们的代码(和测试)更难以维护和重构。
  • 如果发现现有的单元测试违反了这两个规则,则应将其删除。

在本教程中,我们取得了很多成就。 您认为可以使这些单元测试变得更好吗?

如果您想了解有关编写干净测试的更多信息,请阅读我的编写干净测试教程的所有部分 。

翻译自: https://www.javacodegeeks.com/2014/08/writing-clean-tests-to-verify-or-not-to-verify.html

验证码编写

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

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

相关文章

单片机拼字程序怎么做_家装行业做小程序怎么样?

对于家装行业&#xff0c;给大家提到了很多获客引流的方式&#xff0c;无论是线上还是线下&#xff0c;只要坚持去做&#xff0c;最后肯定会取得成果的。关于线下的给大家提到最多的就是合作共赢的模式来拓客&#xff0c;线上主要就是打电话和表单或者进入平台的形式&#xff0…

计算机三级考试网络技术怎么复习,考前数天如何突破性通过计算机等级考试之三级网络技术篇...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼计算机等级考试只有几天的时间了&#xff0c;复习已久的你现在该做些什么?最新出版的书&#xff0c;结合近年等考真题&#xff0c;考三级网络技术的同学需要的可以看看&#xff0c;我找来了目录和内容简介&#xff0c;不错的一本书…

python 删除csv第一行_python 标准库学习之 csv

本文由作者 凭海临风 授权刊登。原文地址&#xff1a;https://jeffsui.github.io/2020/01/02/python-standard-library-csv/文档路径官方文档&#xff1a;https://docs.python.org/zh-cn/3.8/library/csv.htmlCSV 文件格式文件扩展名为.csv&#xff0c;通用的电子表格文件格式&…

Java 8之前和之后的Lambda表达式

Java 8之前和之后的几个示例程序的快速指南。Java8 Concepts进一步简化了代码&#xff0c;并且对核心逻辑&#xff08;而不是语法&#xff09;产生了深远影响。 1.简介 在本教程中&#xff0c;我们将学习如何使用Java 8编写程序 lambda和Stream概念以及示例。 许多开发人员认…

idea java主题_IDEA变身超级形态,需要这个酷炫主题

码个蛋(codeegg) 第 1090 次推文作者&#xff1a;CodeWhite7链接&#xff1a;https://blog.csdn.net/weixin_46146269/article/details/104793277前言IDEA&#xff0c;全称 IntelliJ IDEA &#xff0c;是 Java 语言的集成开发环境&#xff0c; IDEA 在业界被公认为是最好的 jav…

儿童python编程能给孩子带来哪些好处_python编程入门学习对孩子成长有哪些优势?...

python编程语言学习有意义吗&#xff1f; python编程语言是少儿编程培训课程中重要的组成部分&#xff0c;随着越来越多的孩子开始接触和学习编程&#xff0c;通过编程学习培养孩子良好的学习习惯和锻炼提升孩子逻辑思维能力。在python编程培训班学习过程中&#xff0c;通过pyt…

yii2 html ul,yii2导航小部件子菜单类

我使用adminLTE主题进行bootstrap,它使用treeview-menu类来操作子菜单.Nav::widget([options > [class > sidebar-menu treeview],items > [[label > Menu 1, url > [/a/index]],[label > Menu 2, url > [/custom-perks/index]],[label > Submenu, ite…

百度云盘照片导入华为相册里_必须知道的相册管理工具

相信很多人都会在手机上存许多值得回忆的照片&#xff0c;为了避免丢失&#xff0c;我们常常会使用网盘工具来管理我们的照片&#xff0c;例如&#xff0c;比较常用的有百度网盘、天翼云盘、115网盘这三款工具&#xff0c;那么这三款网盘在相册功能上有什么特色呢&#xff1f;今…

微信H5 回调_PHP实现微信H5支付

今头日条一小马奔腾 2019-09-27 18:44:37开发前配置进行代码接入前&#xff0c;需在微信后台填写授权回调域名&#xff0c;此域名必须经过ICP备案开发主要流程用户下单时选择微信支付商户进行业务逻辑处理并调用微信统一下单接口&#xff0c;微信H5交易类型为&#xff1a;trade…

freemarker html 乱码,Freemarker生成静态html文件及中文乱码的问题.pdf

Freemarker生成静态html文件及中文乱码的问题,freemarker静态化页面,freemarker静态化,freemarker乱码,freemarker静态方法,freemarker中文乱码,freemarker网页静态化,freemarker生成word,freemarker代码生成器,freemarker生成htmlFreeMarker 允许 Java servlet 保持图形设计同…

java 泛型嵌套泛型_Java泛型简介–第6部分

java 泛型嵌套泛型这是关于泛型的介绍性讨论的延续&#xff0c; 此处的先前部分可以在此处找到。 在上一篇文章中&#xff0c;我们讨论了关于类型参数的递归边界。 我们看到了递归绑定如何帮助我们重用了车辆比较逻辑。 在该文章的结尾&#xff0c;我建议当我们不够谨慎时&…

优化自定义函数_Pandas常见的性能优化方法

文章来源于Datawhale &#xff0c;作者阿水Pandas是数据科学和数据竞赛中常见的库&#xff0c;我们使用Pandas可以进行快速读取数据、分析数据、构造特征。但Pandas在使用上有一些技巧和需要注意的地方&#xff0c;如果你没有合适的使用&#xff0c;那么Pandas可能运行速度非常…

pycharm cant open file_PyCharm

1.创建项目PyCharm是一种Python IDE.PyCharm的功能到底有多强大&#xff0c;我也说不清楚&#xff0c;今天只是介绍一下关于Pycharm创建项目的问题.✦ PyCharm可以将一个文件夹作为工程(或项目)进行打开.如图1所示&#xff0c;先创建两个文件夹(pyc1和pyc2)&#xff0c;在文件夹…

adminlte支持html5吗,spring boot:用adminlte做前端

标签(空格分隔)&#xff1a; sringboot adminlte thymeleafspring boot 和 adminlte没有紧密的联系&#xff0c;只是最近在做的一个东西用spring boot做后端用adminlte做前端&#xff0c;所以就放到spring boot系列里面讲。架构原理adminlte只是一个库&#xff0c;说不上什么框…

如何通过示例使用Java中的Exchanger

大家好&#xff0c;如果您在并发Java应用程序中工作&#xff0c;那么您可能听说过java.util.concurrent包的Exchanger类。 Java中的Exchanger是Java 1.5中与CountDownLatch &#xff0c; CyclicBarrier和Semaphores一起引入的另一个并发或同步实用程序。 顾名思义&#xff0c; …

python里写在文件的指定行_python文件操作如何写在指定的行

常常在操作文件时我们只想在某一行的插入信息&#xff0c;可以先将文件读入列表中&#xff0c;利用列表的下标插入文本&#xff0c;之后再重新写入文件。但是弊端是&#xff0c;如果文件量太大列表的性能可能不是很高。python代码&#xff1a;#codingutf-8lines[]fopen("d…

外星人跑深度学习_上海港汇外星人店,51M2020开光追和DLSS2.0畅玩《赛博朋克2077》...

上海外星人港汇恒隆广场店是外星人布局上海的首家3.0店面&#xff0c;坐落在繁华的徐家汇商圈港汇恒隆广场南座6楼。门店传承了外星人高端高品质&#xff0c;以服务客户为宗旨&#xff0c;立足上海&#xff0c;辐射周边&#xff0c;服务所有外星人客户。近日&#xff0c;让全球…

python flask html模板,python flask web开发实战 Jinja2模板

templates/index.htmlHello World!templates/user.htmlHello, {{ name }}!渲染模板&#xff1a;from flask import Flask,render_templateapp.route(/)def index():return render_template(index.html)app.route(/user/)def user(name):return render_template(user.html, name…

gwt前台开发_为GWT设置开发环境

gwt前台开发介绍 这是旨在用Java开发跨平台移动应用程序的系列文章的一部分 。 在此博客文章中&#xff0c;我们将了解GWT是什么&#xff0c;并为GWT设置开发环境。 GWT是一个开源开发工具包&#xff0c;用于开发基于浏览器的复杂Ajax应用程序。 使用GWT&#xff0c;您可以用J…

linux 释放进程res_linux内存查看及释放

查看内存常用的查看内存工具有&#xff1a;top&#xff0c;ps&#xff0c;free&#xff0c;/proc/meminfo&#xff0c;/proc/$PID/status等&#xff0c;一般都指定了虚拟内存占用情况&#xff0c;但ps或/proc/$PID/status中RSS或RSZ指定的是实际内存大小。1)freeroot:~# freeto…