日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

specs.4.8.gz_使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试

發布時間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 specs.4.8.gz_使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

specs.4.8.gz

毫無疑問, JAX-RS是一項杰出的技術。 即將發布的規范JAX-RS 2.0帶來了更多的強大功能,尤其是在客戶端API方面。 今天的帖子的主題是JAX-RS服務的集成測試。 有很多優秀的測試框架,例如REST,可以確保提供幫助,但是我要展示的方式是使用富有表現力的BDD風格。 這是我的意思的示例:

Create new person with email <a@b.com>Given REST client for application deployed at http://localhost:8080When I do POST to rest/api/people?email=a@b.com&firstName=Tommy&lastName=KnockerThen I expect HTTP code 201

看起來像現代BDD框架的典型Given / When / Then風格。 使用靜態編譯語言,我們在JVM上能達到多近的距離? 事實證明,非常接近,這要歸功于出色的specs2測試工具。

值得一提的是, specs2是一個Scala框架。 盡管我們將編寫一些Scala ,但是我們將以非常直觀的方式來完成它,這對于經驗豐富的Java開發人員來說是熟悉的。 測試中的JAX-RS服務是我們在上一篇文章中開發的 。 這里是:

package com.example.rs;import java.util.Collection;import javax.inject.Inject; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo;import com.example.model.Person; import com.example.services.PeopleService;@Path( '/people' ) public class PeopleRestService {@Inject private PeopleService peopleService;@Produces( { MediaType.APPLICATION_JSON } )@GETpublic Collection< Person > getPeople( @QueryParam( 'page') @DefaultValue( '1' ) final int page ) {return peopleService.getPeople( page, 5 );}@Produces( { MediaType.APPLICATION_JSON } )@Path( '/{email}' )@GETpublic Person getPeople( @PathParam( 'email' ) final String email ) {return peopleService.getByEmail( email );}@Produces( { MediaType.APPLICATION_JSON } )@POSTpublic Response addPerson( @Context final UriInfo uriInfo,@FormParam( 'email' ) final String email, @FormParam( 'firstName' ) final String firstName, @FormParam( 'lastName' ) final String lastName ) {peopleService.addPerson( email, firstName, lastName );return Response.created( uriInfo.getRequestUriBuilder().path( email ).build() ).build();}@Produces( { MediaType.APPLICATION_JSON } )@Path( '/{email}' )@PUTpublic Person updatePerson( @PathParam( 'email' ) final String email, @FormParam( 'firstName' ) final String firstName, @FormParam( 'lastName' ) final String lastName ) {final Person person = peopleService.getByEmail( email ); if( firstName != null ) {person.setFirstName( firstName );}if( lastName != null ) {person.setLastName( lastName );}return person; }@Path( '/{email}' )@DELETEpublic Response deletePerson( @PathParam( 'email' ) final String email ) {peopleService.removePerson( email );return Response.ok().build();} }

非常簡單的JAX-RS服務來管理人員。 Java實現提供并支持所有基本的HTTP動詞 : GET , PUT , POST和DELETE 。 為了完整起見,讓我也包括服務層的一些方法,因為這些方法引起我們關注的一些例外。

package com.example.services;import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;import org.springframework.stereotype.Service;import com.example.exceptions.PersonAlreadyExistsException; import com.example.exceptions.PersonNotFoundException; import com.example.model.Person;@Service public class PeopleService {private final ConcurrentMap< String, Person > persons = new ConcurrentHashMap< String, Person >(); // ...public Person getByEmail( final String email ) {final Person person = persons.get( email ); if( person == null ) {throw new PersonNotFoundException( email );}return person;}public Person addPerson( final String email, final String firstName, final String lastName ) {final Person person = new Person( email );person.setFirstName( firstName );person.setLastName( lastName );if( persons.putIfAbsent( email, person ) != null ) {throw new PersonAlreadyExistsException( email );}return person;}public void removePerson( final String email ) {if( persons.remove( email ) == null ) {throw new PersonNotFoundException( email );}} }

基于ConcurrentMap的非常簡單但可行的實現。 在沒有請求電子郵件的人的情況下,將引發PersonNotFoundException 。 分別在已經存在帶有請求電子郵件的人的情況下引發PersonAlreadyExistsException 。 這些異常中的每一個在HTTP代碼中都有一個對應項: 404 NOT FOUND和409 CONFLICT 。 這就是我們告訴JAX-RS的方式:

package com.example.exceptions;import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status;public class PersonAlreadyExistsException extends WebApplicationException {private static final long serialVersionUID = 6817489620338221395L;public PersonAlreadyExistsException( final String email ) {super(Response.status( Status.CONFLICT ).entity( 'Person already exists: ' + email ).build());} }package com.example.exceptions;import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status;public class PersonNotFoundException extends WebApplicationException {private static final long serialVersionUID = -2894269137259898072L;public PersonNotFoundException( final String email ) {super(Response.status( Status.NOT_FOUND ).entity( 'Person not found: ' + email ).build());} }

完整的項目托管在GitHub上 。 讓我們結束無聊的部分,然后轉到最甜蜜的部分: BDD 。 如文檔所述, specs2對Given / When / Then樣式提供了很好的支持也就不足為奇了。 因此,使用specs2 ,我們的測試用例將變成這樣:

'Create new person with email <a@b.com>' ^ br^'Given REST client for application deployed at ${http://localhost:8080}' ^ client^'When I do POST to ${rest/api/people}' ^ post(Map('email' -> 'a@b.com', 'firstName' -> 'Tommy', 'lastName' -> 'Knocker'))^'Then I expect HTTP code ${201}' ^ expectResponseCode^'And HTTP header ${Location} to contain ${http://localhost:8080/rest/api/people/a@b.com}' ^ expectResponseHeader^

不錯,但是那些^ , br , client , post , ExpectResponseCode和ExpectResponseHeader是什么? ^ , br只是用于支持Given / When / Then鏈的一些specs2糖。 其他的post , ExpectResponseCode和ExpectResponseHeader只是我們定義用于實際工作的幾個函數/變量。 例如, 客戶端是一個新的JAX-RS 2.0客戶端,我們可以這樣創建(使用Scala語法):

val client: Given[ Client ] = ( baseUrl: String ) => ClientBuilder.newClient( new ClientConfig().property( 'baseUrl', baseUrl ) )

baseUrl來自Given定義本身,它包含在$ {…}構造中。 此外,我們可以看到Given定義具有很強的類型: Given [Client] 。 稍后我們將看到, When和Then的情況相同 ,它們確實具有相應的強類型when [T,V]和Then [V] 。

流程如下所示:

  • 從給定定義開始,該定義返回Client 。
  • 繼續進行When定義,該定義接受Given的 Client并返回Response
  • 最后是數量的Then定義,這些定義接受來自When的 響應并檢查實際期望

這是帖子定義的樣子(本身就是When [Client,Response] ):

def post( values: Map[ String, Any ] ): When[ Client, Response ] = ( client: Client ) => ( url: String ) => client.target( s'${client.getConfiguration.getProperty( 'baseUrl' )}/$url' ).request( MediaType.APPLICATION_JSON ).post( Entity.form( values.foldLeft( new Form() )( ( form, param ) => form.param( param._1, param._2.toString ) ) ),classOf[ Response ] )

最后是ExpectResponseCode和ExpectResponseHeader ,它們非常相似,并且具有相同的類型Then [Response] :

val expectResponseCode: Then[ Response ] = ( response: Response ) => ( code: String ) => response.getStatus() must_== code.toInt val expectResponseHeader: Then[ Response ] = ( response: Response ) => ( header: String, value: String ) => response.getHeaderString( header ) should contain( value )

又一個示例,根據JSON有效負載檢查響應內容:

'Retrieve existing person with email <a@b.com>' ^ br^'Given REST client for application deployed at ${http://localhost:8080}' ^ client^'When I do GET to ${rest/api/people/a@b.com}' ^ get^'Then I expect HTTP code ${200}' ^ expectResponseCode^'And content to contain ${JSON}' ^ expectResponseContent('''{'email': 'a@b.com', 'firstName': 'Tommy', 'lastName': 'Knocker' } ''')^

這次我們使用以下get實現來執行GET請求:

val get: When[ Client, Response ] = ( client: Client ) => ( url: String ) => client.target( s'${client.getConfiguration.getProperty( 'baseUrl' )}/$url' ).request( MediaType.APPLICATION_JSON ).get( classOf[ Response ] )

盡管specs2具有豐富的匹配器集,可對JSON有效負載執行不同的檢查,但我使用的是spray-json ,這是Scala中的輕量,干凈且簡單的JSON實現(的確如此!),這是ExpectResponseContent實現:

def expectResponseContent( json: String ): Then[ Response ] = ( response: Response ) => ( format: String ) => {format match { case 'JSON' => response.readEntity( classOf[ String ] ).asJson must_== json.asJsoncase _ => response.readEntity( classOf[ String ] ) must_== json} }

最后一個示例(對現有電子郵件進行POST ):

'Create yet another person with same email <a@b.com>' ^ br^'Given REST client for application deployed at ${http://localhost:8080}' ^ client^'When I do POST to ${rest/api/people}' ^ post(Map( 'email' -> 'a@b.com' ))^'Then I expect HTTP code ${409}' ^ expectResponseCode^'And content to contain ${Person already exists: a@b.com}' ^ expectResponseContent^

看起來很棒! 好的,富有表現力的BDD ,使用強類型和靜態編譯! 當然,可以使用JUnit集成,并且可以與Eclipse一起很好地工作。

不要忘了自己的specs2報告(由maven-specs2-plugin生成): mvn clean test

請在GitHub上查找完整的項目。 另外,請注意,由于我使用的是最新的JAX-RS 2.0里程碑(最終草案),因此發布該API時可能會有所變化。

參考: Andriy Redko {devmind}博客上的JCG合作伙伴 Andrey Redko 使用Specs2和客戶端API 2.0進行了富有表現力的JAX-RS集成測試 。

翻譯自: https://www.javacodegeeks.com/2013/03/expressive-jax-rs-integration-testing-with-specs2-and-client-api-2-0.html

specs.4.8.gz

總結

以上是生活随笔為你收集整理的specs.4.8.gz_使用Specs2和客户端API 2.0进行富有表现力的JAX-RS集成测试的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。