javascript
在Spring MVC Web应用程序中添加社交登录:集成测试
我已经写了关于为使用Spring Social 1.1.0的应用程序编写单元测试的挑战,并为此提供了一种解决方案 。
尽管单元测试很有价值,但是它并不能真正告诉我们我们的应用程序是否正常运行。
这就是为什么我们必须为此编写集成测试的原因 。
这篇博客文章可以帮助我们做到这一点。 在此博客文章中,我们将学习如何为示例应用程序的注册和登录功能编写集成测试。
如果您尚未阅读Spring Social教程的先前部分,建议您在阅读此博客文章之前先阅读它们。 以下描述了此博客文章的前提条件:
- 在Spring MVC Web应用程序中添加社交登录:配置描述了如何配置示例应用程序。
- 在Spring MVC Web应用程序中添加社交登录:注册和登录介绍了如何向示例应用程序添加注册和登录功能。
- 在Spring MVC Web应用程序中添加社交登录:单元测试描述了如何为示例应用程序编写单元测试。
- Spring MVC测试教程描述了如何使用Spring MVC Test框架编写单元测试和集成测试。
- Spring Data JPA教程:集成测试描述了我们如何为Spring Data JPA存储库编写集成测试。 这篇博客文章帮助您了解如何使用Spring Test DBUnit和DbUnit编写集成测试。
- 使用Maven进行集成测试介绍了如何使用Maven运行集成测试和单元测试。 我们的示例应用程序的构建过程遵循此博客文章中描述的方法。
让我们从对构建过程的配置进行一些更改开始。
配置我们的构建过程
我们必须对构建过程的配置进行以下更改:
让我们找出如何进行这些更改。
将Spring Test DBUnit快照二进制文件添加到本地Maven存储库
由于Spring Test DBUnit的稳定版本与Spring Framework 4不兼容 ,因此我们必须在集成测试中使用构建快照。
我们可以按照以下步骤将Spring Test DBUnit快照添加到本地Maven存储库:
将jar文件复制到本地Maven存储库后,我们必须在pom.xml文件中配置本地存储库的位置。 我们可以通过将以下存储库声明添加到我们的POM文件中来做到这一点:
<repositories><!-- Other repositories are omitted for the sake of clarity --><repository><id>local-repository</id><name>local repository</name><url>file://${project.basedir}/etc/mavenrepo</url></repository> </repositories>使用Maven获取所需的测试依赖项
通过将以下依赖项声明添加到我们的POM文件中,我们可以获得所需的测试依赖项:
- Spring测试DBUnit (版本1.1.1-SNAPSHOT)。 我们使用Spring Test DBUnit将Spring Test框架与DbUnit库集成在一起。
- DbUnit (版本2.4.9)。 在每次集成测试之前,我们使用DbUnit将数据库初始化为已知状态,并验证数据库的内容是否与预期数据匹配。
- liquibase-core (版本3.1.1)。 加载集成测试的应用程序上下文时,我们使用Liquibase创建一些数据库表。
pom.xml文件的相关部分如下所示:
<dependency><groupId>com.github.springtestdbunit</groupId><artifactId>spring-test-dbunit</artifactId><version>1.1.1-SNAPSHOT</version><scope>test</scope> </dependency> <dependency><groupId>org.dbunit</groupId><artifactId>dbunit</artifactId><version>2.4.9</version><scope>test</scope> </dependency> <dependency><groupId>org.liquibase</groupId><artifactId>liquibase-core</artifactId><version>3.1.1</version><scope>test</scope> </dependency>将Liquibase变更集添加到类路径
通常,我们应该让Hibernate创建用于集成测试的数据库。 但是,仅当在我们的域模型中配置了每个数据库表时,此方法才有效。
现在不是这种情况。 示例应用程序的数据库具有一个UserConnection表,该表未在示例应用程序的域模型中配置。 这就是为什么我们需要在运行集成测试之前找到另一种方法来创建UserConnection表。
为此,我们可以使用Liquibase库的Spring集成,但这意味着我们必须将Liquibase变更集添加到类路径中。
我们可以通过使用Build Helper Maven插件来实现 。 我们可以按照以下步骤将Liquibase变更集添加到类路径中:
插件配置的相关部分如下所示:
<plugin><groupId>org.codehaus.mojo</groupId><artifactId>build-helper-maven-plugin</artifactId><version>1.7</version><executions><!-- Other executions are omitted for the sake of clarity --><execution><id>add-integration-test-resources</id><!-- Run this execution in the generate-test-sources lifecycle phase --><phase>generate-test-resources</phase><goals><!-- Invoke the add-test-resource goal of this plugin --><goal>add-test-resource</goal></goals><configuration><resources><!-- Other resources are omitted for the sake of clarity --><!-- Add the directory which contains Liquibase change sets to classpath --><resource><directory>etc/db</directory></resource></resources></configuration></execution></executions> </plugin>如果要获取有关使用Builder Helper Maven插件的用法的更多信息,可以查看以下网页:
- 与Maven的集成测试
- Builder Helper Maven插件
现在,我们已经完成了构建过程的配置。 让我们找出如何配置集成测试。
配置我们的集成测试
我们可以按照以下步骤配置集成测试:
让我们继续前进,仔细看看每个步骤。
修改Liquibase变更日志
我们的示例应用程序有两个Liquibase变更集,可从etc / db / schema目录中找到。 这些变更集是:
因为我们只想运行第一个变更集,所以我们必须对Liquibase变更日志文件进行一些修改。 更具体地说,我们必须使用Liquibase上下文来指定
通过执行以下步骤,我们可以实现我们的目标:
我们的Liquibase changelog文件如下所示:
<?xml version="1.0" encoding="UTF-8"?> <databaseChangeLogxmlns="http://www.liquibase.org/xml/ns/dbchangelog"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd"><!-- Run this change set when the database is created and integration tests are run --><changeSet id="0.0.1" author="Petri" context="db,integrationtest"><sqlFile path="schema/db-0.0.1.sql" /></changeSet><!-- Run this change set when the database is created --><changeSet id="0.0.2" author="Petri" context="db"><sqlFile path="schema/db-0.0.2.sql" /></changeSet> </databaseChangeLog>在运行集成测试之前执行Liquibase变更集
通过在加载应用程序上下文时执行集成测试,我们可以在运行集成测试之前执行Liquibase变更集。 我们可以按照以下步骤进行操作:
IntegrationTestContext类的源代码如下所示:
import liquibase.integration.spring.SpringLiquibase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;@Configuration public class IntegrationTestContext {@Autowiredprivate DataSource dataSource;@Beanpublic SpringLiquibase liquibase() {SpringLiquibase liquibase = new SpringLiquibase();liquibase.setDataSource(dataSource);liquibase.setChangeLog("classpath:changelog.xml");liquibase.setContexts("integrationtest");return liquibase;} }创建一个自定义DataSetLoader类
包含不同用户帐户信息的DbUnit数据集如下所示:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts id="1"creation_time="2014-02-20 11:13:28"email="facebook@socialuser.com"first_name="Facebook"last_name="User"modification_time="2014-02-20 11:13:28"role="ROLE_USER"sign_in_provider="FACEBOOK"version="0"/><user_accounts id="2"creation_time="2014-02-20 11:13:28"email="twitter@socialuser.com"first_name="Twitter"last_name="User"modification_time="2014-02-20 11:13:28"role="ROLE_USER"sign_in_provider="TWITTER"version="0"/><user_accounts id="3"creation_time="2014-02-20 11:13:28"email="registered@user.com"first_name="RegisteredUser"last_name="User"modification_time="2014-02-20 11:13:28"password="$2a$10$PFSfOaC2IFPG.1HjO05KoODRVSdESQ5q7ek4IyzVfTf.VWlKDa/.e"role="ROLE_USER"version="0"/><UserConnection/> </dataset>我们可以从该数据集中看到两件事:
这是一个问题,因为我们使用所谓的平面XML数据集 ,而默认的DbUnit数据集加载器无法处理这种情况 。 当然,我们可以开始使用标准XML数据集,但就我的口味而言,其语法有点过于冗长。 这就是为什么我们必须创建一个可以处理这种情况的自定义数据集加载器的原因。
我们可以按照以下步骤创建自定义数据集加载器:
ColumnSensingFlatXMLDataSetLoader类的源代码如下所示:
import com.github.springtestdbunit.dataset.AbstractDataSetLoader; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.springframework.core.io.Resource;import java.io.InputStream;public class ColumnSensingFlatXMLDataSetLoader extends AbstractDataSetLoader {@Overrideprotected IDataSet createDataSet(Resource resource) throws Exception {FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();builder.setColumnSensing(true);InputStream inputStream = resource.getInputStream();try {return builder.build(inputStream);} finally {inputStream.close();}} }但是,创建自定义数据集加载器类还不够。 加载数据集时,我们仍然必须配置测试以使用此类。 我们可以通过使用@DbUnitConfiguration批注注释测试类并将其dataSetLoader属性的值设置为ColumnSensingFlatXMLDataSetLoader.class来实现此目的 。
让我们继续看这是如何完成的。
配置我们的集成测试
我们可以按照以下步骤配置集成测试:
空测试类的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.web.FilterChainProxy; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; 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 = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) public class ITTest {@Autowiredprivate FilterChainProxy springSecurityFilterChain;@Autowiredprivate WebApplicationContext webApplicationContext;private MockMvc mockMvc;@Beforepublic void setUp() {mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilter(springSecurityFilterChain).build();} }如果您需要有关我们的集成测试的配置的更多信息,建议您阅读以下博客文章:
- Spring MVC控制器的单元测试:配置介绍了如何配置Spring MVC Test框架。 本教程讨论了单元测试,但仍应进一步阐明该问题。
- Spring Data JPA教程:集成测试描述了如何为Spring Data JPA存储库编写集成测试。 如果您想看一下Spring Test DBUnit的配置,这篇博客文章可能有助于您理解它。
- Spring MVC应用程序的集成测试:安全性描述了如何编写Spring MVC应用程序的安全性测试。 本教程基于Spring Security 3.1,但它仍然可以帮助您了解如何编写这些测试。
现在,我们已经了解了如何配置集成测试。 让我们继续并创建一些在集成测试中使用的测试实用程序类。
创建测试实用程序类
接下来,我们将创建在集成测试中使用的三个实用程序类:
让我们找出为什么我们必须创建这些类以及如何创建它们。
创建IntegrationTestConstants类
当我们编写集成(或单元)测试时,有时我们需要在许多测试类中使用相同的信息。 将这些信息复制到所有测试类是一个坏主意,因为这会使我们的测试难以维护和理解。 相反,我们应该将此信息放在一个类中,并在需要时从该类中获取。
IntegrationTestConstants类包含以下信息,这些信息可在多个测试类中使用:
- 它具有与Spring Security 3.2的CSRF保护相关的常数。 这些常量包括:包含CSRF令牌的HTTP标头的名称,包含CSRF令牌的值的请求参数的名称,包含CsrfToken对象的会话属性的名称以及CSRF令牌的值。
- 它包含User枚举,该枚举指定了我们的集成测试中使用的用户。 每个用户都有一个用户名和密码(这不是必需的)。 该枚举的信息用于两个目的:
- 用于指定登录用户。 当我们对受保护的功能(需要某种授权的功能)进行集成测试时,这很有用。
- 在为登录功能编写集成测试时,我们需要指定尝试登录该应用程序的用户的用户名和密码。
IntegrationTestConstants类的源代码如下所示:
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;public class IntegrationTestConstants {public static final String CSRF_TOKEN_HEADER_NAME = "X-CSRF-TOKEN";public static final String CSRF_TOKEN_REQUEST_PARAM_NAME = "_csrf";public static final String CSRF_TOKEN_SESSION_ATTRIBUTE_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");public static final String CSRF_TOKEN_VALUE = "f416e226-bebc-401d-a1ed-f10f47aa9c56";public enum User {FACEBOOK_USER("facebook@socialuser.com", null),REGISTERED_USER("registered@user.com", "password"),TWITTER_USER("twitter@socialuser.com", null);private String password;private String username;private User(String username, String password) {this.password = password;this.username = username;}public String getPassword() {return password;}public String getUsername() {return username;}} }创建ProviderSignInAttempt对象
在为示例应用程序编写单元测试时,我们快速浏览了ProviderSignInUtils类,并意识到我们必须找到一种创建ProviderSignInAttempt对象的方法。
我们通过创建在单元测试中使用的存根类来解决该问题。 这个存根类使我们可以配置返回的Connection <?>对象,并验证特定的连接是否“持久化到数据库”。 但是,我们的存根类未持久连接到使用的数据库。 而是将用户的用户ID存储到Set对象。
因为现在我们想将连接数据持久化到数据库,所以我们必须对存根类进行更改。 我们可以通过对TestProviderSignInAttempt对象进行以下更改来进行这些更改:
TestProviderSignInAttempt类的源代码如下所示(修改的部分突出显示):
import org.springframework.social.connect.Connection; import org.springframework.social.connect.UsersConnectionRepository;import java.util.HashSet; import java.util.Set;public class TestProviderSignInAttempt extends ProviderSignInAttempt {private Connection<?> connection;private Set<String> connections = new HashSet<>();private boolean usersConnectionRepositorySet = false;public TestProviderSignInAttempt(Connection<?> connection, UsersConnectionRepository usersConnectionRepository) {super(connection, null, usersConnectionRepository);this.connection = connection;if (usersConnectionRepository != null) {this.usersConnectionRepositorySet = true;}}@Overridepublic Connection<?> getConnection() {return connection;}@Overridevoid addConnection(String userId) {connections.add(userId);if (usersConnectionRepositorySet) {super.addConnection(userId);}}public Set<String> getConnections() {return connections;} }因为我们通过使用TestProviderSignInAttemptBuilder来构建新的TestProviderSignInAttempt对象,所以我们也必须对该类进行更改。 我们可以按照以下步骤进行这些更改:
TestProviderSignInAttemptBuilder类的源代码如下所示(修改的部分突出显示):
import org.springframework.social.connect.*; import org.springframework.social.connect.web.TestProviderSignInAttempt;public class TestProviderSignInAttemptBuilder {private String accessToken;private String displayName;private String email;private Long expireTime;private String firstName;private String imageUrl;private String lastName;private String profileUrl;private String providerId;private String providerUserId;private String refreshToken;private String secret;private UsersConnectionRepository usersConnectionRepository;public TestProviderSignInAttemptBuilder() {}public TestProviderSignInAttemptBuilder accessToken(String accessToken) {this.accessToken = accessToken;return this;}public TestProviderSignInAttemptBuilder connectionData() {return this;}public TestProviderSignInAttemptBuilder displayName(String displayName) {this.displayName = displayName;return this;}public TestProviderSignInAttemptBuilder email(String email) {this.email = email;return this;}public TestProviderSignInAttemptBuilder expireTime(Long expireTime) {this.expireTime = expireTime;return this;}public TestProviderSignInAttemptBuilder firstName(String firstName) {this.firstName = firstName;return this;}public TestProviderSignInAttemptBuilder imageUrl(String imageUrl) {this.imageUrl = imageUrl;return this;}public TestProviderSignInAttemptBuilder lastName(String lastName) {this.lastName = lastName;return this;}public TestProviderSignInAttemptBuilder profileUrl(String profileUrl) {this.profileUrl = profileUrl;return this;}public TestProviderSignInAttemptBuilder providerId(String providerId) {this.providerId = providerId;return this;}public TestProviderSignInAttemptBuilder providerUserId(String providerUserId) {this.providerUserId = providerUserId;return this;}public TestProviderSignInAttemptBuilder refreshToken(String refreshToken) {this.refreshToken = refreshToken;return this;}public TestProviderSignInAttemptBuilder secret(String secret) {this.secret = secret;return this;}public TestProviderSignInAttemptBuilder usersConnectionRepository(UsersConnectionRepository usersConnectionRepository) {this.usersConnectionRepository = usersConnectionRepository;return this;}public TestProviderSignInAttemptBuilder userProfile() {return this;}public TestProviderSignInAttempt build() {ConnectionData connectionData = new ConnectionData(providerId,providerUserId,displayName,profileUrl,imageUrl,accessToken,secret,refreshToken,expireTime);UserProfile userProfile = new UserProfileBuilder().setEmail(email).setFirstName(firstName).setLastName(lastName).build();Connection connection = new TestConnection(connectionData, userProfile);return new TestProviderSignInAttempt(connection, usersConnectionRepository);} }创建CsrfToken对象
因为我们的示例应用程序使用了Spring Security 3.2提供的CSRF保护,所以我们必须找出一种在集成测试中创建有效CSRF令牌的方法。 CsrfToken接口声明了提供有关预期CSRF令牌信息的方法。 该接口具有一个称为DefaultCsrfToken的实现。
换句话说,我们必须找出一种创建新的DefaultCsrfToken对象的方法。 DefaultCsrfToken类只有一个构造函数 ,在集成测试中创建新的DefaultCsrfToken对象时,我们当然可以使用它。 问题是这不是很可读。
相反,我们将创建一个测试数据构建器类 , 该类提供了用于创建新CsrfToken对象的流利API 。 我们可以按照以下步骤创建此类:
CsrfTokenBuilder类的源代码如下所示:
import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.DefaultCsrfToken;public class CsrfTokenBuilder {private String headerName;private String requestParameterName;private String tokenValue;public CsrfTokenBuilder() {}public CsrfTokenBuilder headerName(String headerName) {this.headerName = headerName;return this;}public CsrfTokenBuilder requestParameterName(String requestParameterName) {this.requestParameterName = requestParameterName;return this;}public CsrfTokenBuilder tokenValue(String tokenValue) {this.tokenValue = tokenValue;return this;}public CsrfToken build() {return new DefaultCsrfToken(headerName, requestParameterName, tokenValue);} }我们可以使用以下代码创建新的CsrfToken对象:
CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();现在,我们已经创建了必需的测试实用程序类。 让我们继续并开始为示例应用程序编写集成测试。
编写集成测试
我们终于准备好为示例应用程序编写一些集成测试。 我们将编写以下集成测试:
- 我们将编写集成测试,以确保表单登录正常工作。
- 我们将编写集成测试,以验证使用社交登录时注册是否正常运行。
但是在开始编写这些集成测试之前,我们将学习如何为Spring Security提供有效的CSRF令牌。
向Spring Security提供有效的CSRF令牌
之前我们了解了如何在集成测试中创建CsrfToken对象。 但是,我们仍然必须找出一种将这些CSRF令牌提供给Spring Security的方法。
现在是时候仔细研究一下Spring Security处理CSRF令牌的方式了。
CsrfTokenRepository接口声明生成,保存和加载CSRF令牌所需的方法。 该接口的默认实现是HttpSessionCsrfTokenRepository类,该类将CSRF令牌存储到HTTP会话。
我们需要找到以下问题的答案:
- CSRF令牌如何保存到HTTP会话?
- 如何从HTTP会话加载CSRF令牌?
通过查看HttpSessionCsrfTokenRepository类的源代码,我们可以找到这些问题的答案。 HttpSessionCsrfTokenRepository类的相关部分如下所示:
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;public void saveToken(CsrfToken token, HttpServletRequest request,HttpServletResponse response) {if (token == null) {HttpSession session = request.getSession(false);if (session != null) {session.removeAttribute(sessionAttributeName);}} else {HttpSession session = request.getSession();session.setAttribute(sessionAttributeName, token);}}public CsrfToken loadToken(HttpServletRequest request) {HttpSession session = request.getSession(false);if (session == null) {return null;}return (CsrfToken) session.getAttribute(sessionAttributeName);}//Other methods are omitted. }现在很清楚,CSRF令牌作为CsrfToken对象存储到HTTP会话,并且使用sessionAttributeName属性的值重试和存储这些对象。 这意味着,如果我们想向Spring Security提供有效的CSRF令牌,则必须遵循以下步骤:
我们的虚拟测试的源代码如下所示:
import org.junit.Test; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.DefaultCsrfToken; import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;public class ITCSRFTest {private MockMvc mockMvc;@Testpublic void test() throws Exception {//1. Create a new CSRF tokenCsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/login/authenticate")//2. Send the value of the CSRF token as request parameter.param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE)//3. Set the created CsrfToken object to session so that the CsrfTokenRepository finds it.sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken))//Add assertions here.} }理论足够了。 现在,我们准备为我们的应用程序编写一些集成测试。 首先,将集成编写到示例应用程序的登录功能中。
编写登录功能测试
现在该为示例应用程序的登录功能编写集成测试了。 我们将为此编写以下集成测试:
这两个集成测试都使用相同的DbUnit数据集文件( users.xml )将数据库初始化为已知状态,其内容如下所示:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts id="1"creation_time="2014-02-20 11:13:28"email="facebook@socialuser.com"first_name="Facebook"last_name="User"modification_time="2014-02-20 11:13:28"role="ROLE_USER"sign_in_provider="FACEBOOK"version="0"/><user_accounts id="2"creation_time="2014-02-20 11:13:28"email="twitter@socialuser.com"first_name="Twitter"last_name="User"modification_time="2014-02-20 11:13:28"role="ROLE_USER"sign_in_provider="TWITTER"version="0"/><user_accounts id="3"creation_time="2014-02-20 11:13:28"email="registered@user.com"first_name="RegisteredUser"last_name="User"modification_time="2014-02-20 11:13:28"password="$2a$10$PFSfOaC2IFPG.1HjO05KoODRVSdESQ5q7ek4IyzVfTf.VWlKDa/.e"role="ROLE_USER"version="0"/><UserConnection/> </dataset>让我们开始吧。
测试1:登录成功
我们可以按照以下步骤编写第一个集成测试:
我们的集成测试的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) @DatabaseSetup("/net/petrikainulainen/spring/social/signinmvc/user/users.xml") public class ITFormLoginTest {private static final String REQUEST_PARAM_PASSWORD = "password";private static final String REQUEST_PARAM_USERNAME = "username";//Some fields are omitted for the sake of clarityprivate MockMvc mockMvc;//The setUp() method is omitted for the sake of clarify.@Testpublic void login_CredentialsAreCorrect_ShouldRedirectUserToFrontPage() throws Exception {CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/login/authenticate").param(REQUEST_PARAM_USERNAME, IntegrationTestConstants.User.REGISTERED_USER.getUsername()).param(REQUEST_PARAM_PASSWORD, IntegrationTestConstants.User.REGISTERED_USER.getPassword()).param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE).sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken)).andExpect(status().isMovedTemporarily()).andExpect(redirectedUrl("/"));} }测试2:登录失败
我们可以按照以下步骤编写第二个集成测试:
我们的集成测试的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) @DatabaseSetup("/net/petrikainulainen/spring/social/signinmvc/user/users.xml") public class ITFormLoginTest {private static final String REQUEST_PARAM_PASSWORD = "password";private static final String REQUEST_PARAM_USERNAME = "username";//Some fields are omitted for the sake of clarityprivate MockMvc mockMvc;//The setUp() method is omitted for the sake of clarify.@Testpublic void login_InvalidPassword_ShouldRedirectUserToLoginForm() throws Exception {CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/login/authenticate").param(REQUEST_PARAM_USERNAME, IntegrationTestConstants.User.REGISTERED_USER.getUsername()).param(REQUEST_PARAM_PASSWORD, "invalidPassword").param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE).sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken)).andExpect(status().isMovedTemporarily()).andExpect(redirectedUrl("/login?error=bad_credentials"));} }为注册功能编写测试
我们将为注册功能编写以下集成测试:
让我们开始吧。
测试1:验证失败
我们可以按照以下步骤编写第一个集成测试:
我们的集成测试的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import com.github.springtestdbunit.assertion.DatabaseAssertionMode; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.social.connect.UsersConnectionRepository; import org.springframework.social.connect.support.TestProviderSignInAttemptBuilder; import org.springframework.social.connect.web.ProviderSignInAttempt; import org.springframework.social.connect.web.TestProviderSignInAttempt; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static net.petrikainulainen.spring.social.signinmvc.user.controller.TestProviderSignInAttemptAssert.assertThatSignIn; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) public class ITRegistrationControllerTest {@Autowiredprivate UsersConnectionRepository usersConnectionRepository;//Some fields are omitted for the sake of clarity.private MockMvc mockMvc;//The setUp() is omitted for the sake of clarity.@Test@DatabaseSetup("no-users.xml")@ExpectedDatabase(value="no-users.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)public void registerUserAccount_SocialSignInAndEmptyForm_ShouldRenderRegistrationFormWithValidationErrors() throws Exception {TestProviderSignInAttempt socialSignIn = new TestProviderSignInAttemptBuilder().connectionData().accessToken("accessToken").displayName("John Smith").expireTime(100000L).imageUrl("https://www.twitter.com/images/johnsmith.jpg").profileUrl("https://www.twitter.com/johnsmith").providerId("twitter").providerUserId("johnsmith").refreshToken("refreshToken").secret("secret").usersConnectionRepository(usersConnectionRepository).userProfile().email("john.smith@gmail.com").firstName("John").lastName("Smith").build();RegistrationForm userAccountData = new RegistrationFormBuilder().signInProvider(SocialMediaService.TWITTER).build();CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/user/register").contentType(MediaType.APPLICATION_FORM_URLENCODED).content(TestUtil.convertObjectToFormUrlEncodedBytes(userAccountData)).sessionAttr(ProviderSignInAttempt.SESSION_ATTRIBUTE, socialSignIn).param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE).sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken).sessionAttr("user", userAccountData)).andExpect(status().isOk()).andExpect(view().name("user/registrationForm")).andExpect(forwardedUrl("/WEB-INF/jsp/user/registrationForm.jsp")).andExpect(model().attribute("user", allOf(hasProperty("email", isEmptyOrNullString()),hasProperty("firstName", isEmptyOrNullString()),hasProperty("lastName", isEmptyOrNullString()),hasProperty("password", isEmptyOrNullString()),hasProperty("passwordVerification", isEmptyOrNullString()),hasProperty("signInProvider", is(SocialMediaService.TWITTER))))).andExpect(model().attributeHasFieldErrors("user", "email", "firstName", "lastName"));} }我们的集成测试使用一个名为no-users.xml的DbUnit数据集文件,该文件如下所示:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts/><UserConnection/> </dataset>测试2:从数据库中找到电子邮件地址
我们可以按照以下步骤编写第二个集成测试:
我们的集成测试的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import com.github.springtestdbunit.assertion.DatabaseAssertionMode; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.social.connect.UsersConnectionRepository; import org.springframework.social.connect.support.TestProviderSignInAttemptBuilder; import org.springframework.social.connect.web.ProviderSignInAttempt; import org.springframework.social.connect.web.TestProviderSignInAttempt; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) public class ITRegistrationControllerTest {@Autowiredprivate UsersConnectionRepository usersConnectionRepository;//Some fields are omitted for the sake of clarity.private MockMvc mockMvc;//The setUp() is omitted for the sake of clarity.@Test@DatabaseSetup("/net/petrikainulainen/spring/social/signinmvc/user/users.xml")@ExpectedDatabase(value = "/net/petrikainulainen/spring/social/signinmvc/user/users.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)public void registerUserAccount_SocialSignInAndEmailExist_ShouldRenderRegistrationFormWithFieldError() throws Exception {TestProviderSignInAttempt socialSignIn = new TestProviderSignInAttemptBuilder().connectionData().accessToken("accessToken").displayName("John Smith").expireTime(100000L).imageUrl("https://www.twitter.com/images/johnsmith.jpg").profileUrl("https://www.twitter.com/johnsmith").providerId("twitter").providerUserId("johnsmith").refreshToken("refreshToken").secret("secret").usersConnectionRepository(usersConnectionRepository).userProfile().email(IntegrationTestConstants.User.REGISTERED_USER.getUsername()).firstName("John").lastName("Smith").build();RegistrationForm userAccountData = new RegistrationFormBuilder().email(IntegrationTestConstants.User.REGISTERED_USER.getUsername()).firstName("John").lastName("Smith").signInProvider(SocialMediaService.TWITTER).build();CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/user/register").contentType(MediaType.APPLICATION_FORM_URLENCODED).content(TestUtil.convertObjectToFormUrlEncodedBytes(userAccountData)).sessionAttr(ProviderSignInAttempt.SESSION_ATTRIBUTE, socialSignIn).param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE).sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken).sessionAttr("user", userAccountData)).andExpect(status().isOk()).andExpect(view().name("user/registrationForm")).andExpect(forwardedUrl("/WEB-INF/jsp/user/registrationForm.jsp")).andExpect(model().attribute("user", allOf(hasProperty("email", is(IntegrationTestConstants.User.REGISTERED_USER.getUsername())),hasProperty("firstName", is("John")),hasProperty("lastName", is("Smith")),hasProperty("password", isEmptyOrNullString()),hasProperty("passwordVerification", isEmptyOrNullString()),hasProperty("signInProvider", is(SocialMediaService.TWITTER))))).andExpect(model().attributeHasFieldErrors("user", "email"));} }此集成测试使用一个名为users.xml的DbUnit数据集,该数据集如下所示:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts id="1" creation_time="2014-02-20 11:13:28" email="facebook@socialuser.com" first_name="Facebook" last_name="User" modification_time="2014-02-20 11:13:28" role="ROLE_USER" sign_in_provider="FACEBOOK" version="0"/><user_accounts id="2" creation_time="2014-02-20 11:13:28" email="twitter@socialuser.com" first_name="Twitter" last_name="User" modification_time="2014-02-20 11:13:28" role="ROLE_USER" sign_in_provider="TWITTER" version="0"/><user_accounts id="3" creation_time="2014-02-20 11:13:28" email="registered@user.com" first_name="RegisteredUser" last_name="User" modification_time="2014-02-20 11:13:28" password="$2a$10$PFSfOaC2IFPG.1HjO05KoODRVSdESQ5q7ek4IyzVfTf.VWlKDa/.e" role="ROLE_USER" version="0"/><UserConnection/> </dataset>测试3:注册成功
我们可以按照以下步骤编写第三项集成测试:
我们的集成测试的源代码如下所示:
import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import com.github.springtestdbunit.assertion.DatabaseAssertionMode; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.social.connect.UsersConnectionRepository; import org.springframework.social.connect.support.TestProviderSignInAttemptBuilder; import org.springframework.social.connect.web.ProviderSignInAttempt; import org.springframework.social.connect.web.TestProviderSignInAttempt; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {ExampleApplicationContext.class, IntegrationTestContext.class}) //@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"}) @WebAppConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) public class ITRegistrationControllerTest2 {@Autowiredprivate UsersConnectionRepository usersConnectionRepository;//Some fields are omitted for the sake of clarity.private MockMvc mockMvc;//The setUp() is omitted for the sake of clarity.@Test@DatabaseSetup("no-users.xml")@ExpectedDatabase(value="register-social-user-expected.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)public void registerUserAccount_SocialSignIn_ShouldCreateNewUserAccountAndRenderHomePage() throws Exception {TestProviderSignInAttempt socialSignIn = new TestProviderSignInAttemptBuilder().connectionData().accessToken("accessToken").displayName("John Smith").expireTime(100000L).imageUrl("https://www.twitter.com/images/johnsmith.jpg").profileUrl("https://www.twitter.com/johnsmith").providerId("twitter").providerUserId("johnsmith").refreshToken("refreshToken").secret("secret").usersConnectionRepository(usersConnectionRepository).userProfile().email("john.smith@gmail.com").firstName("John").lastName("Smith").build();RegistrationForm userAccountData = new RegistrationFormBuilder().email("john.smith@gmail.com").firstName("John").lastName("Smith").signInProvider(SocialMediaService.TWITTER).build();CsrfToken csrfToken = new CsrfTokenBuilder().headerName(IntegrationTestConstants.CSRF_TOKEN_HEADER_NAME).requestParameterName(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME).tokenValue(IntegrationTestConstants.CSRF_TOKEN_VALUE).build();mockMvc.perform(post("/user/register").contentType(MediaType.APPLICATION_FORM_URLENCODED).content(TestUtil.convertObjectToFormUrlEncodedBytes(userAccountData)).sessionAttr(ProviderSignInAttempt.SESSION_ATTRIBUTE, socialSignIn).param(IntegrationTestConstants.CSRF_TOKEN_REQUEST_PARAM_NAME, IntegrationTestConstants.CSRF_TOKEN_VALUE).sessionAttr(IntegrationTestConstants.CSRF_TOKEN_SESSION_ATTRIBUTE_NAME, csrfToken).sessionAttr("user", userAccountData)).andExpect(status().isMovedTemporarily()).andExpect(redirectedUrl("/"));} }用于将数据库初始化为已知状态的数据集( no-users.xml )如下所示:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts/><UserConnection/> </dataset>名为register-social-user-expected.xml的DbUnit数据集用于验证是否成功创建了用户帐户,并且与使用的社交登录提供者的连接已持久保存到数据库中。 它看起来如下:
<?xml version='1.0' encoding='UTF-8'?> <dataset><user_accounts email="john.smith@gmail.com" first_name="John" last_name="Smith" role="ROLE_USER" sign_in_provider="TWITTER"version="0"/><UserConnection userId="john.smith@gmail.com"providerId="twitter"providerUserId="johnsmith"rank="1"displayName="John Smith"profileUrl="https://www.twitter.com/johnsmith"imageUrl="https://www.twitter.com/images/johnsmith.jpg"accessToken="accessToken"secret="secret"refreshToken="refreshToken"expireTime="100000"/> </dataset>摘要
现在,我们已经了解了如何为使用Spring Social 1.1.0的常规Spring MVC应用程序编写集成测试。 本教程教会了我们很多东西,但是这两件事是本博客文章的主要课程:
- 我们了解了如何通过创建ProviderSignInAttempt对象并在集成测试中使用它们来“模拟”社交登录。
- 我们学习了如何创建CSRF令牌并将创建的令牌提供给Spring Security。
让我们花点时间来分析此博客文章中描述的方法的优缺点:
优点:
- 我们可以编写集成测试,而无需使用外部社交登录提供程序。 这使我们的测试不那么脆弱,更易于维护。
- Spring Social( ProviderSignInAttempt )和Spring Security CSRF保护( CsrfToken )的实现细节被“隐藏”以测试数据构建器类。 这使我们的测试更具可读性,更易于维护。
缺点:
- 本教程没有描述我们如何编写社交登录集成测试(使用社交登录提供程序登录)。 我试图找出一种无需使用外部登录提供程序即可编写这些测试的方法,但我只是用光了时间(这似乎很复杂,我想发布此博客文章)。
这篇博客文章结束了我的“向Spring MVC应用程序添加社交登录”教程。
我将写一个类似的教程,描述将来如何在社会支持的REST API中添加社交登录。 同时,您可能需要阅读本教程的其他部分 。
- 您可以从Github获得此博客文章的示例应用程序。
翻译自: https://www.javacodegeeks.com/2014/03/adding-social-sign-in-to-a-spring-mvc-web-application-integration-testing.html
总结
以上是生活随笔为你收集整理的在Spring MVC Web应用程序中添加社交登录:集成测试的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 在Java EE 7和WildFly中使
- 下一篇: Spring应用程序与JNDI连接池的集