精华内容
下载资源
问答
  • 词法分析程序 用c语言编写 包含了各种功能 简单明了
  • 词法分析器c语言编写

    2008-11-25 15:24:14
    c语言编写词法分析器,挺好用的,我已调试过
  • 词法分析器c语言编写 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的“干净”一词定义。 但是,有一个似乎是通用的定义: 简洁的代码易于阅读。 这可能会让您感到有些惊讶,但我认为该定义也适用于...
    词法分析器c语言编写

    词法分析器c语言编写

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

    简洁的代码易于阅读。

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

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

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

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

    这是我教程的第一部分,描述了我们如何编写干净的测试。 这次,我们将学习如何以简单干净的方式配置测试用例。

    问题

    假设我们必须使用Spring MVC Test框架为Spring MVC控制器编写“单元测试”。 我们要测试的第一个控制器称为TodoController ,但是我们还必须为应用程序的其他控制器编写“单元测试”。

    作为开发人员,我们知道重复的代码是一件坏事。 在编写代码时,我们遵循“不要重复自己(DRY)”原则,该原则指出:

    每条知识都必须在系统中具有单一,明确,权威的表示形式。

    我怀疑这是开发人员经常在其测试套件中使用继承的原因之一。 他们将继承视为重用代码和配置的廉价且简便的方法。 这就是为什么他们将所有通用代码和配置放入实际测试类的一个或多个基类。

    让我们看看如何使用该方法配置“单元测试”。

    首先,我们必须创建一个抽象基类, 基类可以配置Spring MVC Test框架,并通过实现setUpTest(MockMvc mockMvc)方法来确保其子类可以提供其他配置。

    AbstractControllerTest类的源代码如下所示:

    import org.junit.Before;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {WebUnitTestContext.class})
    @WebAppConfiguration
    public abstract class AbstractControllerTest {
    
        private MockMvc mockMvc;
    
        @Autowired
        private WebApplicationContext webAppContext;
    
        @Before
        public void setUp() {
            mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
            setupTest(MockMvc mockMvc)
        }
       
        protected abstract void setUpTest(MockMvc mockMvc);
    }

    其次,我们必须实现实际的测试类,该类创建所需的模拟和新的控制器对象。 TodoControllerTest类的源代码如下所示:

    import org.mockito.Mockito;
    import org.springframework.test.web.servlet.MockMvc;
    
    public class TodoControllerTest extends AbstractControllerTest {
    
        private MockMvc mockMvc;
    
        @Autowired
        private TodoService serviceMock;
       
        @Override
        protected void setUpTest(MockMvc mockMvc) {
            Mockito.reset(serviceMock);
            this.mockMvc = mockMvc;
        }
    
        //Add test methods here
    }

    这个测试类看起来很干净,但是有一个主要缺陷:

    如果我们想了解测试用例的配置方式,则必须阅读TodoControllerTestAbstractControllerTest类的源代码。

    这似乎是一个小问题,但这意味着我们必须将注意力从测试用例转移到一个或多个基类上。 这需要精神上的上下文切换,并且上下文切换非常昂贵

    您可能当然会争辩说,在这种情况下使用继承的精神代价很低,因为配置非常简单。 的确如此,但是要记住,现实生活中的情况并非总是如此。

    上下文切换的实际成本取决于测试类层次结构的深度和配置的复杂性。

    解决方案

    我们可以通过配置测试类中的所有测试用例来提高配置的可读性。 这意味着我们必须:

    • 将所需的注释(例如@RunWith )添加到测试类。
    • 将设置和拆卸方法添加到测试类。

    如果我们遵循以下规则修改示例测试类,则其源代码如下:

    import org.junit.Before;
    import org.junit.runner.RunWith;
    import org.mockito.Mockito;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {WebUnitTestContext.class})
    @WebAppConfiguration
    public class TodoControllerTest {
    
        private MockMvc mockMvc;
       
        @Autowired
        private TodoService serviceMock;
    
        @Autowired
        private WebApplicationContext webAppContext;
    
        @Before
        public void setUp() {
            Mockito.reset(serviceMock);
            mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
        }
    
        //Add test methods here
    }

    在我看来,我们的测试用例的新配置看起来比旧的配置简单且干净得多,旧的配置分为TodoControllerTestAbstractControllerTest类。

    不幸的是,没有什么是免费的。

    这是一个权衡

    每个软件设计决策都需要权衡利弊。 这不是该规则的例外

    在测试类中配置我们的测试用例具有以下好处:

    1. 我们无需阅读测试类的所有超类就可以了解测试用例的配置。 这样可以节省大量时间,因为我们不必将注意力从一堂课转移到另一堂课。 换句话说,我们不必付出上下文切换的代价
    2. 当测试失败时,可以节省时间。 如果为了避免重复的代码或配置而使用继承,则很可能我们的基类将包含与某些但不是全部测试用例相关的组件。 换句话说,我们将弄清楚哪些组件与失败的测试用例相关,这可能不是一件容易的事。 在测试类中配置测试用例时,我们知道每个组件都与失败的测试用例有关

    另一方面,这种方法的缺点是:

    1. 我们必须编写重复的代码。 这比将所需的配置放到一个或多个基类上花费的时间更长。
    2. 如果任何使用的库以迫使我们修改测试配置的方式进行更改,则我们必须对每个测试类进行必要的更改。 这显然比仅对基类(或多个基类)进行这些操作要慢得多。

    如果我们唯一的目标是尽可能快地编写测试,那么很明显,我们应该消除重复的代码和配置。

    但是,这不是我唯一的目标。

    我认为这种方法的好处胜过缺点的原因有两个:

    1. 继承不是重用代码或配置的正确工具
    2. 如果测试用例失败,我们必须尽快找到并解决问题,并且干净的配置将帮助我们实现该目标。

    我在这件事上的立场是明确的。 但是,仍然存在一个非常重要的问题:

    您会做出其他折衷吗?

    翻译自: https://www.javacodegeeks.com/2014/05/writing-clean-tests-it-starts-from-the-configuration.html

    词法分析器c语言编写

    展开全文
  • 词法分析器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语言编写

    展开全文
  • 词法分析器c语言编写 当我们向数据访问代码编写测试时,是否应该测试其公共API的每种方法? 一开始听起来很自然。 毕竟,如果我们不对所有内容进行测试,那么如何知道我们的代码可以按预期工作呢? 这个问题为我们...
    词法分析器c语言编写

    词法分析器c语言编写

    当我们向数据访问代码编写测试时,是否应该测试其公共API的每种方法?

    一开始听起来很自然。 毕竟,如果我们不对所有内容进行测试,那么如何知道我们的代码可以按预期工作呢?

    这个问题为我们提供了重要的线索:

    我们的代码

    我们应该只对自己的代码编写测试。

    什么是我们自己的代码?

    有时很难确定我们应该测试的代码。 这是因为我们的数据访问代码与将信息保存到使用的数据存储中或从中读取信息时所使用的库或框架紧密集成在一起。

    例如,如果我们要创建一个向Todo对象提供CRUD操作的Spring Data JPA存储库,则应创建一个扩展CrudRepository接口的接口。 TodoRepository接口的源代码如下所示:

    import org.springframework.data.repository.CrudRepository;
    
    public TodoRepository extends CrudRepository<Todo, Long> {
    
    }

    即使我们没有向存储库接口添加任何方法, CrudRepository接口也声明了许多可供使用我们存储库接口的类使用的方法。

    这些方法不是我们的代码,因为它们是由Spring Data团队实现和维护的。 我们只使用它们。

    另一方面,如果我们向存储库中添加自定义查询方法,情况将会改变。 假设我们必须找到标题等于给定搜索词的所有待办事项。 在将此查询方法添加到我们的存储库接口之后,其源代码如下所示:

    import org.springframework.data.repository.CrudRepository;
    import org.springframework.data.repository.query.Param;
    
    public TodoRepository extends CrudRepository<Todo, Long> {
    
    	@Query("SELECT t FROM Todo t where t.title=:searchTerm")
    	public List<Todo> search(@Param("searchTerm") String searchTerm)
    }

    可以很容易地断言该方法是我们自己的代码,这就是为什么我们应该对其进行测试。 但是,事实有点复杂。 即使JPQL查询是由我们编写的,Spring Data JPA也会提供将查询转发给使用过的JPA提供程序的代码。

    而且,我仍然认为该查询方法是我们自己的代码,因为其中最重要的部分是由我们编写的。

    如果要标识自己的数据访问代码,则必须找到每种方法的基本部分。 如果这部分是我们编写的,则应将该方法视为自己的代码。

    这一切都是显而易见的,更有趣的问题是:

    我们应该测试吗?

    我们的存储库接口为使用它的类提供了两种方法:

    1. 它提供了由CrudRepository接口声明的方法。
    2. 它提供了我们编写的查询方法。

    我们是否应该将集成测试编写到TodoRepository接口并测试所有这些方法?

    不,我们不应该这样做,因为

    1. CrudRepository接口声明的方法不是我们自己的代码。 这段代码是由Spring Data团队编写和维护的,他们已经确保它可以工作。 如果我们不相信他们的代码有效,那么我们就不应该使用它。
    2. 我们的应用程序可能有许多存储库接口,这些接口扩展了CrudRepository接口。 如果决定对CrudRepository接口声明的方法编写测试,则必须对所有存储库编写这些测试。 如果选择这种方式,我们将花费大量时间对其他人的代码编写测试,坦率地说,这样做是不值得的。
    3. 我们自己的代码可能是如此简单,以至于无法将测试写入我们的存储库。

    换句话说,我们应该集中精力寻找这个问题的答案:

    我们应该将集成测试写入我们的存储库方法(由我们编写的方法),还是只编写端到端测试?

    这个问题的答案取决于我们存储库方法的复杂性。 我知道复杂性是一个模糊的词,这就是为什么我们需要某种准则来帮助我们找到测试存储库方法的最佳方法的原因。

    做出此决定的一种方法是考虑测试每种可能情况所需的工作量。 这是有道理的,因为:

    1. 将集成测试写入单个存储库方法比将相同的测试写入使用存储库方法的功能所需的工作更少。
    2. 无论如何,我们都必须端对端地编写。

    这就是为什么最小化我们的投资(时间)和最大化我们的利润(测试覆盖率)的原因。 我们可以按照以下规则进行操作:

    • 如果我们只编写几个测试就可以测试所有可能的场景,那么我们就不应该浪费时间将集成测试写入我们的存储库方法。 我们应该编写端到端测试,以确保该功能按预期工作。
    • 如果我们需要编写多个测试,则应将集成测试编写到我们的存储库方法中,而仅编写一些端到端测试(烟雾测试)。

    概要

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

    • 我们不应该浪费时间将测试编写到其他人编写的数据访问框架(或库)中。 如果我们不信任该框架(或库),则不应使用它。
    • 有时我们也不应该对数据访问代码编写集成测试。 如果经过测试的代码足够简单(我们可以通过编写一些测试来涵盖所有情况),则应该通过编写端到端测试来对其进行测试。

    翻译自: https://www.javacodegeeks.com/2014/07/writing-tests-for-data-access-code-dont-test-the-framework.html

    词法分析器c语言编写

    展开全文
  • 词法分析器c语言编写 如果我们的代码有明显的错误,那么我们就有动力去改进它。 但是,在某些时候,我们认为我们的代码“足够好”并继续前进。 通常,当我们认为改进现有代码的好处小于所需的工作时,就会发生这种...
    词法分析器c语言编写

    词法分析器c语言编写

    如果我们的代码有明显的错误,那么我们就有动力去改进它。 但是,在某些时候,我们认为我们的代码“足够好”并继续前进。

    通常,当我们认为改进现有代码的好处小于所需的工作时,就会发生这种情况。 当然,如果我们低估了投资回报,我们可能会打错电话,这会伤害我们。

    这就是发生在我身上的事情,因此我决定写这篇文章,以便您避免犯同样的错误。

    编写“良好”单元测试

    如果我们要编写“良好”的单元测试,则必须编写以下单元测试:

    • 只测试一件事 一个好的单元测试只能由于一个原因而失败,并且只能断言一件事。
    • 被正确命名 测试方法的名称必须揭示测试失败的原因。
    • 模拟外部依赖关系(和状态) 如果单元测试失败,我们将确切知道问题出在哪里。

    补充阅读:

    如果我们编写满足这些条件的单元测试,我们将编写好的单元测试。 对?

    我曾经这样认为。 现在我对此表示怀疑

    善意铺平地狱之路

    我从未见过一个决定编写糟糕的单元测试的软件开发人员。 如果开发人员正在编写单元测试,则他/她更有可能要编写好的单元测试。 但是,这并不意味着该开发人员编写的单元测试是好的。

    我想编写既易于阅读又易于维护的单元测试。 我什至写了一个教程,描述了如何编写干净的测试 问题在于,本教程中给出的建议还不够好。 它可以帮助我们入门,但是并没有显示出兔子洞的真正深度。

    我的教程中描述的方法存在两个主要问题:

    命名标准FTW?

    如果我们使用Roy Osherove引入“命名标准”,则会注意到很难描述被测状态和预期行为。

    当我们为简单场景编写测试时,此命名标准非常有效。 问题在于,真正的软件并不简单。 通常,我们最终使用以下两个选项之一来命名测试方法:

    首先,如果我们尝试尽可能具体,那么我们的测试方法的方法名称就显得太过looooooooong。 最后,我们必须承认我们不能像我们想要的那样具体,因为方法名称会占用太多空间。

    其次,如果我们尝试使方法名称尽可能短,则方法名称将不会真正描述测试状态和预期行为。

    选择哪个选项并不重要,因为无论如何我们都会遇到以下问题:

    • 如果测试失败,则方法名称不一定描述要出错的方法。 我们可以使用自定义断言来解决此问题,但是它们不是免费的。
    • 很难对我们的测试涵盖的场景进行简要概述。

    以下是我们在“编写干净测试”教程中编写的测试方法的名称:

    • registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldThrowException()
    • registerNewUserAccount_SocialSignInAndDuplicateEmail_ShouldNotSaveNewUserAccount()
    • registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldSaveNewUserAccountAndSetSignInProvider()
    • registerNewUserAccount_SocialSignInAndUniqueEmail_ShouldReturnCreatedUserAccount()
    • registerNewUserAccount_SocialSignInAnquequeEmail_ShouldNotCreateEncodedPasswordForUser()

    这些方法的名称不是很长,但是我们必须记住,编写这些单元测试是为了测试一种简单的注册方法。 当我使用这种命名约定为现实生活中的软件项目编写自动化测试时,最长的方法名称是我们最长的示例的两倍。

    那不是很干净或可读。 我们可以做得更好

    没有通用配置

    在本教程中,我们使单元测试变得更好了 尽管如此,他们仍然遭受这样的事实,即没有“自然的”方式在不同的单元测试之间共享配置。

    这意味着我们的单元测试包含许多重复的代码,这些代码配置了我们的模拟对象并创建了在单元测试中使用的其他对象。

    同样,由于没有“自然”的方式表明某些常量仅与特定的测试方法相关,因此我们必须将所有常量添加到测试类的开头。

    我们的测试类的源代码如下(突出显示有问题的代码):

    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);
        }
    }

    一些开发人员认为看起来像上面示例的单元测试足够干净。 我理解这种情绪,因为我曾经是其中之一。 但是,这些单元测试有三个问题:

    1. 该案的实质不是很清楚 因为每种测试方法在调用被测试方法并验证预期结果之前都会进行自我配置,所以我们的测试方法变得比必要的更长。 这意味着我们不能只看一眼随机测试方法并弄清楚它要测试什么。
    2. 编写新的单元测试很慢 因为每个单元测试都必须自行配置,所以向我们的测试套件中添加新的单元测试比它可能要慢得多。 另一个“意外”的缺点是,这种单元测试鼓励人们练习复制和粘贴编程
    3. 维持这些单元测试是一件痛苦的事情 如果我们向注册表单添加新的必填字段,或者更改registerNewUserAccount()方法的实现,则必须对每个单元测试进行更改。 这些单元测试太脆弱了。

    换句话说,这些单元测试很难阅读,很难编写和维护。 我们必须做得更好

    概要

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

    • 即使我们认为我们正在编写好的单元测试,也不一定是正确的。
    • 如果由于必须更改许多单元测试而导致更改现有功能的速度很慢,那么我们就不会编写好的单元测试。
    • 如果由于我们必须向单元测试中添加大量重复代码而导致添加新功能的速度很慢,那么我们就不会编写好的单元测试。
    • 如果我们看不到单元测试所涵盖的情况,那么我们就没有编写好的单元测试。

    本教程的下一部分将回答这个非常相关的问题:

    如果我们现有的单元测试很烂,我们该如何解决?

    如果要编写干净的测试,则应阅读我的“编写干净的测试”教程

    翻译自: https://www.javacodegeeks.com/2015/03/writing-clean-tests-trouble-in-paradise.html

    词法分析器c语言编写

    展开全文
  • 词法分析器c语言编写 很难为干净的代码找到一个好的定义,因为我们每个人都有自己的“干净”一词定义。 但是,有一个似乎是通用的定义: 简洁的代码易于阅读。 这可能会让您感到有些惊讶,但我认为该定义也适用于...
  • 通过C语言编写一个词法分析器完成输入是字符串(或源程序文本文件),输出是源程序中各单词的字符串、起止位置、词的类别。附带实验报告。
  • 编译原理简单的词法分析器c语言编写 词法分析器 C语言 编译原理 何炎祥 词法分析器实验
  • 编译原理实验 词法分析器 语法分析器 完美代码 完美实验
  • 词法分析器 c语言

    2009-06-18 15:43:27
    一个用C语言编写词法分析器,功能很全,与课本同步
  • 编译原理,词法分析器C语言编写,将编写的代码放在txt文本中读取。完美运行。注意放文件的位置
  • 编译原理课上 编写的 可用 功能可以自己完善
  • 编译原理大作业 含FA图 java词法分析器 C语言编写
  • 一个简单的词法分析器 #include<iostream> #include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; //存放处理后的字符串 char tempstr[255]= {}; //空格标志 bool...
  • C语言编写词法分析器源码 免费赠送 不是本人编写 有问题的话见谅
  • 《编译原理》词法分析程序设计方案,用C语言编写词法分析器
  • 包含源代码,设计说明书以及.C文件。简单易懂,内附详细说明
  • 词法分析器 编译原理实验 c语言
  • C语言编写词法分析器

    2008-12-14 12:53:02
    用C 语言编写词法分析器 运行成功的 ~ 对学习编译原理有帮助
  • 编译原理实验一(C-语言词法分析器编写C语言版本) 1.词法规则 ①语言的关键字: else if int return void while 所有的关键字都是保留字,并且必须是小写。 ②专用符号: + - * / < <= > &...
  • 该程序是用C语言编写的简单的词法分析器,在学习编译原理课程的时候你可能会用到它!
  • c语言编写词法分析器,简单容易理解!
  • c语言编写词法分析器 编译原理的课程设计类文旦

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 207
精华内容 82
关键字:

词法分析器c语言编写

c语言 订阅