STOMP 消息传递 && Testing With Spring

1.Spring 的 STOMP 消息传递

STOMP 消息传递来创建交互式 Web 应用程序。 STOMP 是一个运行在底层 WebSocket 之上的子协议。

使用Gradle,需要添加以下依赖:

implementation 'org.springframework.boot:spring-boot-starter-websocket'

implementation 'org.webjars:webjars-locator-core'

implementation 'org.webjars:sockjs-client:1.0.2'

implementation 'org.webjars:stomp-websocket:2.3.3'

implementation 'org.webjars:bootstrap:3.3.7'

implementation 'org.webjars:jquery:3.1.1-1'

testImplementation 'org.springframework.boot:spring-boot-starter-test'

1.1请求


请求为:

{

"name": "kenychen16888"

}

1.2请求代码为:

@MessageMapping("/hello")

@SendTo("/topic/greetings")

public Greeting greeting(HelloMessage message) throws Exception {

Thread.sleep(1000); // simulated delay

return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");

}

1.3返回是

{

"content": "Hello, kenychen16888!"

}

1.4Create a Message-handling Controller 控制层

package com.example.messagingstompwebsocket;

import org.springframework.messaging.handler.annotation.MessageMapping;

import org.springframework.messaging.handler.annotation.SendTo;

import org.springframework.stereotype.Controller;

import org.springframework.web.util.HtmlUtils;

@Controller

public class GreetingController {

@MessageMapping("/hello")

@SendTo("/topic/greetings")

public Greetinggreeting(HelloMessage message)throws Exception {

Thread.sleep(1000); // simulated delay

        return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) +"!");

    }

}

1.5在idea点击运行


idea运行界面


1.6运行后


运行界面

点击Connect按钮,再输入what’s your name 输入,kenychen ,就获取到结果为Hello, kenychen!

1.7恭喜!您刚刚使用 Spring 开发了一个基于 STOMP 的消息服务。


2.Testing in Spring Boot

我们将了解如何使用 Spring Boot 中的框架支持编写测试。我们将介绍可以独立运行的单元测试以及在执行测试之前引导 Spring 上下文的集成测试。我们将在本文中使用的应用程序是一个 API,它提供了对 Employee 资源的一些基本操作。这是典型的分层架构——API 调用从 Controller 到 Service 再到 Persistence 层进行处理。

spring-boot-starter-test 是主要依赖项,包含我们测试所需的大部分元素。

H2 DB 是我们的内存数据库。它消除了为测试目的配置和启动实际数据库的需要。

先要建立一个使用 Spring 构建 REST 服务项目为了测试上面

Spring Boot 可以与任何 IDE 一起使用。您可以使用 Eclipse、IntelliJ IDEA、Netbeans 等。Spring Tool Suite 是一个开源的、基于 Eclipse 的 IDE 发行版,它提供了 Eclipse 的 Java EE 发行版的超集。它包括使使用 Spring 应用程序更加容易的功能。它绝不是必需的。但是,如果您想要为您的击键增加额外的魅力,请考虑一下。这是一个演示如何开始使用 STS 和 Spring Boot 的视频。这是让您熟悉这些工具的一般性介绍

2.1定义一个Employee 类

package com.kenychen.sprintcrudrest;

import java.util.Objects;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

@Entity

class Employee {

private @Id @GeneratedValue Longid;

    private Stringname;

    private Stringrole;

    Employee() {}

Employee(String name, String role) {

this.name = name;

        this.role = role;

    }

public LonggetId() {

return this.id;

    }

public StringgetName() {

return this.name;

    }

public StringgetRole() {

return this.role;

    }

public void setId(Long id) {

this.id = id;

    }

public void setName(String name) {

this.name = name;

    }

public void setRole(String role) {

this.role = role;

    }

@Override

    public boolean equals(Object o) {

if (this == o)

return true;

        if (!(oinstanceof Employee))

return false;

        Employee employee = (Employee) o;

        return Objects.equals(this.id, employee.id) && Objects.equals(this.name, employee.name)

&& Objects.equals(this.role, employee.role);

    }

@Override

    public int hashCode() {

return Objects.hash(this.id, this.name, this.role);

    }

@Override

    public StringtoString() {

return "Employee{" +"id=" +this.id +", name='" +this.name +'\'' +", role='" +this.role +'\'' +'}';

    }

}

@Entity 是一个 JPA 注释,用于使该对象准备好存储在基于 JPA 的数据存储中。

id、name 和 role 是我们的 Employee 域对象的属性。 id 用更多 JPA 注释标记以表明它是主键并由 JPA 提供程序自动填充。

当我们需要创建一个新实例但还没有 id 时,就会创建一个自定义构造函数。

2.2定义一个EmployeeRepository 

package com.kenychen.sprintcrudrest;

import org.springframework.data.jpa.repository.JpaRepository;

interface EmployeeRepository extends JpaRepository {

}

创建新员工

更新现有的

删除员工

查找员工(一个、全部或按简单或复杂属性搜索)

2.3 定义主函数SprintCrudRestApplication

package com.kenychen.sprintcrudrest;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class SprintCrudRestApplication {

public static void main(String[] args) {

SpringApplication.run(SprintCrudRestApplication.class, args);

    }

}

2.4 定义LoadDatabase

package com.kenychen.sprintcrudrest;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.boot.CommandLineRunner;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

class LoadDatabase {

private static final Loggerlog = LoggerFactory.getLogger(LoadDatabase.class);

    @Bean

    CommandLineRunnerinitDatabase(EmployeeRepository repository) {

return args -> {

log.info("Preloading " +repository.save(new Employee("keny chen", "burglar")));

            log.info("Preloading " +repository.save(new Employee("chen boss", "thief")));

        };

    }

}

2.5 运行主函数


主函数运行

2.6 HTTP 是平台

 spring mvc EmployeeController

package com.kenychen.sprintcrudrest.Controller;

import java.util.List;

import com.kenychen.sprintcrudrest.EmployeeRepository;

import com.kenychen.sprintcrudrest.Model.Employee;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RestController;

@RestController

class EmployeeController {

private final EmployeeRepositoryrepository;

    EmployeeController(EmployeeRepository repository) {

this.repository = repository;

    }

// Aggregate root

// tag::get-aggregate-root[]

    @GetMapping("/employees")

Listall() {

return repository.findAll();

    }

// end::get-aggregate-root[]

    @PostMapping("/employees")

EmployeenewEmployee(@RequestBody Employee newEmployee) {

return repository.save(newEmployee);

    }

// Single item

    @GetMapping("/employees/{id}")

Employeeone(@PathVariable Long id) {

return repository.findById(id)

.orElseThrow(() ->new EmployeeNotFoundException(id));

    }

@PutMapping("/employees/{id}")

EmployeereplaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) {

return repository.findById(id)

.map(employee -> {

employee.setFirstname(newEmployee.getFirstname());

                    employee.setRole(newEmployee.getRole());

                    return repository.save(employee);

                })

.orElseGet(() -> {

newEmployee.setId(id);

                    return repository.save(newEmployee);

                });

    }

@DeleteMapping("/employees/{id}")

void deleteEmployee(@PathVariable Long id) {

repository.deleteById(id);

    }

}

2.7.定义一个model

package com.kenychen.sprintcrudrest.Model;

import java.util.Objects;

import javax.persistence.Entity;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

@Entity

public class Employee {

private @Id @GeneratedValue Longid;

    private Stringfirstname;

    private Stringlastname;

    private Stringrole;

    public Employee() {}

public Employee(String firstName,String lastName, String role) {

this.firstname = firstName;

        this.lastname =lastName;

        this.role = role;

    }

public LonggetId() {

return this.id;

    }

public StringgetFirstname() {

return this.firstname ;

    }

public StringgetLastname() {

return this.lastname ;

    }

public StringgetRole() {

return this.role;

    }

public void setId(Long id) {

this.id = id;

    }

public void setFirstname(String name) {

this.firstname = name;

    }

public void setLastname(String name) {

this.lastname = name;

    }

public void setRole(String role) {

this.role = role;

    }

@Override

    public boolean equals(Object o) {

if (this == o)

return true;

        if (!(oinstanceof Employee))

return false;

        Employee employee = (Employee) o;

        return Objects.equals(this.id, employee.id) && Objects.equals(this.firstname, employee.firstname)

&& Objects.equals(this.role, employee.role);

    }

@Override

    public int hashCode() {

return Objects.hash(this.id, this.firstname, this.role);

    }

@Override

    public StringtoString() {

return "Employee{" +"id=" +this.id +", name='" +this.firstname +'\'' +", role='" +this.role +'\'' +'}';

    }

}

2.8.定义一个EmployeeRepository extends 

package com.kenychen.sprintcrudrest;

import com.kenychen.sprintcrudrest.Model.Employee;

import org.springframework.data.jpa.repository.JpaRepository;

public interface EmployeeRepository extends JpaRepository {

}

2.9 运行查询雇员


查询

通过postman可以进行post请求


post添加一个记录


查询

2.9使用@SpringBootTest 进行集成测试

当我们需要引导整个容器时,@SpringBootTest 注释很有用。注释通过创建将在我们的测试中使用的 ApplicationContext 来工作。

我们可以使用@SpringBootTest的webEnvironment属性来配置我们的运行环境;我们在这里使用 WebEnvironment.MOCK 以便容器将在模拟 servlet 环境中运行。

接下来,@TestPropertySource 注释有助于配置特定于我们的测试的属性文件的位置。请注意,使用@TestPropertySource 加载的属性文件将覆盖现有的 application.properties 文件。

application-integrationtest.properties 包含配置持久性存储的详细信息

package com.kenychen.sprintcrudrest.Controller;

import com.kenychen.sprintcrudrest.EmployeeRepository;

import com.kenychen.sprintcrudrest.SprintCrudRestApplication;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.TestPropertySource;

import org.springframework.test.context.junit4.SpringRunner;

import org.springframework.test.web.servlet.MockMvc;

@RunWith(SpringRunner.class)

@SpringBootTest(

        classes = SprintCrudRestApplication.class)

@AutoConfigureMockMvc

@TestPropertySource(

locations ="classpath:integrationtest.properties")

public class EmployeeRepositoryIntegrationTest{

@Autowired

    private MockMvcmvc;

    @Autowired

    private EmployeeRepositoryrepository;

    // write test cases here

@Test

public void whenFindByName_thenReturnEmployee() {

// given

    Employee alex =new Employee("alex","alex","alexrole");

    entityManager.persist(alex);

    entityManager.flush();

    //when

    String name = alex.getName();

    Optional any =employeeRepository.findAll().stream().filter(employee -> employee.getName().equals(name)).findAny();

    if (any.isPresent()){

System.out.println(any.get().getName());

        assertThat(any.get().getName())

.isEqualTo(alex.getName());

    }

}

}

编写一个简单新雇员,然后再进行查询

查看日志

2021-07-21 18:13:21.021 INFO 224300 --- [ Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : Starting EmployeeRepositoryIntegrationTest using Java 16 on KENY-PC with PID 224300 (started by Dev in D:\spring\sprint-crud-rest)

2021-07-21 18:13:21.024  INFO 224300 --- [    Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : No active profile set, falling back to default profiles: default

2021-07-21 18:13:21.393  INFO 224300 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.

2021-07-21 18:13:21.448  INFO 224300 --- [    Test worker] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 48 ms. Found 2 JPA repository interfaces.

2021-07-21 18:13:21.508  INFO 224300 --- [    Test worker] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version

2021-07-21 18:13:21.677  INFO 224300 --- [    Test worker] o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:44725134-c7b2-441b-ab6d-bf5651821193;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'

2021-07-21 18:13:21.972  INFO 224300 --- [    Test worker] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]

2021-07-21 18:13:22.026  INFO 224300 --- [    Test worker] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.32.Final

2021-07-21 18:13:22.158  INFO 224300 --- [    Test worker] o.hibernate.annotations.common.Version  : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}

2021-07-21 18:13:22.269  INFO 224300 --- [    Test worker] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect

Hibernate: drop table if exists customer_order CASCADE

Hibernate: drop table if exists employee CASCADE

Hibernate: drop sequence if exists hibernate_sequence

Hibernate: create sequence hibernate_sequence start with 1 increment by 1

Hibernate: create table customer_order (id bigint not null, description varchar(255), status integer, primary key (id))

Hibernate: create table employee (id bigint not null, first_name varchar(255), last_name varchar(255), role varchar(255), primary key (id))

2021-07-21 18:13:22.790  INFO 224300 --- [    Test worker] o.h.e.t.j.p.i.JtaPlatformInitiator      : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]

2021-07-21 18:13:22.798  INFO 224300 --- [    Test worker] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'

2021-07-21 18:13:23.156  INFO 224300 --- [    Test worker] .k.s.C.EmployeeRepositoryIntegrationTest : Started EmployeeRepositoryIntegrationTest in 2.444 seconds (JVM running for 3.43)

2021-07-21 18:13:23.180  INFO 224300 --- [    Test worker] o.s.t.c.transaction.TransactionContext  : Began transaction (1) for test context [DefaultTestContext@382ca419 testClass = EmployeeRepositoryIntegrationTest, testInstance = com.kenychen.sprintcrudrest.Controller.EmployeeRepositoryIntegrationTest@aecb8e8, testMethod = whenFindByName_thenReturnEmployee@EmployeeRepositoryIntegrationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@ac5130e testClass = EmployeeRepositoryIntegrationTest, locations = '{}', classes = '{class com.kenychen.sprintcrudrest.SprintCrudRestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1d84677e, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@3b6a052e, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b5055a9f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3777b63, [ImportsContextCustomizer@1ab08501 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4cc7f642, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7d258e97, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@355df7a0]; rollback [true]

Hibernate: call next value for hibernate_sequence

Hibernate: insert into employee (first_name, last_name, role, id) values (?, ?, ?, ?)

Hibernate: select employee0_.id as id1_1_, employee0_.first_name as first_na2_1_, employee0_.last_name as last_nam3_1_, employee0_.role as role4_1_ from employee employee0_

alex alex

2021-07-21 18:13:23.501  INFO 224300 --- [    Test worker] o.s.t.c.transaction.TransactionContext  : Rolled back transaction for test: [DefaultTestContext@382ca419 testClass = EmployeeRepositoryIntegrationTest, testInstance = com.kenychen.sprintcrudrest.Controller.EmployeeRepositoryIntegrationTest@aecb8e8, testMethod = whenFindByName_thenReturnEmployee@EmployeeRepositoryIntegrationTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@ac5130e testClass = EmployeeRepositoryIntegrationTest, locations = '{}', classes = '{class com.kenychen.sprintcrudrest.SprintCrudRestApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@1d84677e, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@3b6a052e, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@b5055a9f, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3777b63, [ImportsContextCustomizer@1ab08501 key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@4cc7f642, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@7d258e97, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]

2021-07-21 18:13:23.512  INFO 224300 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'

2021-07-21 18:13:23.512  INFO 224300 --- [ionShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'

Hibernate: drop table if exists customer_order CASCADE

Hibernate: drop table if exists employee CASCADE

Hibernate: drop sequence if exists hibernate_sequence

BUILD SUCCESSFUL in 4s


你可能感兴趣的:(STOMP 消息传递 && Testing With Spring)