词法分析器c语言编写_编写干净的测试–验证或不验证

词法分析器c语言编写

词法分析器c语言编写

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

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

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

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

对?

让我们验证一切

让我们从服务方法的实现开始,该服务方法用于向数据库添加新的用户帐户。

此服务方法的要求是:

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

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

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

  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;

    @Autowired
    public RepositoryUserService(PasswordEncoder passwordEncoder, UserRepository repository) {
        this.passwordEncoder = passwordEncoder;
        this.repository = repository;
    }

    @Transactional
    @Override
    public 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;

    @Mock
    private PasswordEncoder passwordEncoder;

    @Mock
    private UserRepository repository;

    @Before
    public void setUp() {
        registrationService = new RepositoryUserService(passwordEncoder, repository);
    }

    @Test
    public 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);
    }

    @Test
    public 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);
    }

    @Test
    public 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));
    }

    @Test
    public 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);
    }

    @Test
    public 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);
    }

    @Test
    public 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);
    }


    @Test
    public 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>() {
            @Override
            public 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);
    }

    @Test
    public 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;

    @Mock
    private PasswordEncoder passwordEncoder;

    @Mock
    private UserRepository repository;

    @Before
    public void setUp() {
        registrationService = new RepositoryUserService(passwordEncoder, repository);
    }

    @Test
    public 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);
    }

    @Test
    public 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));
    }

    @Test
    public 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);
    }


    @Test
    public 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>() {
            @Override
            public 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);
    }

    @Test
    public 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

词法分析器c语言编写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
词法分析器是编译器中的一部分,它的主要作用是将源代码分割成一个个的单词(token),并将其转化为一个有意义的符号串。在C语言中,词法分析器需要识别标识符、关键字、运算符、常量等。 利用自动机理论实现词法分析器可以分为以下几个步骤: 1. 定义词法单元(token)的类型和属性,例如标识符、关键字、常量等。 2. 根据C语言的语法规则,设计自动机的状态转移图。 3. 实现自动机的状态转移函数,将输入的字符序列转化为词法单元。 4. 对于每个识别出来的词法单元,需要记录其类型和属性值,可以采用符号表来实现。 5. 最后生成词法单元序列,作为后续语法分析的输入。 下面是一个简单的C语言词法分析器的实现(仅包括标识符、关键字、常量和运算符的识别): ```c #include <stdio.h> #include <ctype.h> #define MAX_TOKEN_LEN 100 // 定义词法单元类型 typedef enum { TOKEN_KEYWORD, TOKEN_IDENTIFIER, TOKEN_CONSTANT, TOKEN_OPERATOR, TOKEN_UNKNOWN } TokenType; // 定义词法单元结构体 typedef struct { TokenType type; char value[MAX_TOKEN_LEN + 1]; } Token; // 关键字表 char *keywords[] = {"int", "float", "if", "else", "while", "for"}; // 符号表 Token sym_table[MAX_TOKEN_LEN]; // 当前输入缓冲区 char input_buffer[MAX_TOKEN_LEN]; // 当前输入缓冲区位置 int input_pos = 0; // 获取下一个字符 char next_char() { return input_buffer[input_pos++]; } // 将指针回退一个字符 void unget_char() { input_pos--; } // 判断是否为关键字 int is_keyword(char *str) { int i; for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { if (strcmp(str, keywords[i]) == 0) { return 1; } } return 0; } // 识别标识符 Token get_identifier() { Token token; token.type = TOKEN_IDENTIFIER; int i = 0; while (isalnum(input_buffer[input_pos])) { token.value[i++] = next_char(); } token.value[i] = '\0'; if (is_keyword(token.value)) { token.type = TOKEN_KEYWORD; } return token; } // 识别常量 Token get_constant() { Token token; token.type = TOKEN_CONSTANT; int i = 0; while (isdigit(input_buffer[input_pos])) { token.value[i++] = next_char(); } token.value[i] = '\0'; return token; } // 识别运算符 Token get_operator() { Token token; token.type = TOKEN_OPERATOR; token.value[0] = next_char(); token.value[1] = '\0'; return token; } // 词法分析函数 Token *lex() { Token *tokens = NULL; Token token; while (input_pos < strlen(input_buffer)) { char c = next_char(); if (isalpha(c)) { unget_char(); token = get_identifier(); } else if (isdigit(c)) { unget_char(); token = get_constant(); } else if (c == '+' || c == '-' || c == '*' || c == '/') { token = get_operator(); } else { token.type = TOKEN_UNKNOWN; token.value[0] = c; token.value[1] = '\0'; } sym_table[sizeof(sym_table) / sizeof(sym_table[0])] = token; } return tokens; } int main() { // 读入源代码 fgets(input_buffer, MAX_TOKEN_LEN, stdin); // 进行词法分析 Token *tokens = lex(); // 输出词法单元序列 int i; for (i = 0; i < sizeof(sym_table) / sizeof(sym_table[0]); i++) { if (sym_table[i].type == TOKEN_UNKNOWN) { printf("Unknown token: %s\n", sym_table[i].value); } else { printf("Token type: %d, Token value: %s\n", sym_table[i].type, sym_table[i].value); } } return 0; } ``` 以上代码实现了一个简单的C语言词法分析器,可以识别标识符、关键字、常量和运算符,并将其存入符号表中。在实际应用中,还需要考虑更多的细节和特殊情况,例如注释、字符串等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值