ทดสอบ JAX-RS Web Service หรือไม่


84

ฉันกำลังมองหาวิธีสร้างการทดสอบอัตโนมัติสำหรับบริการเว็บที่ใช้JAX-RS (Java API สำหรับ RESTful Web Services)

โดยพื้นฐานแล้วฉันต้องการวิธีส่งปัจจัยการผลิตบางอย่างและตรวจสอบว่าฉันได้รับคำตอบที่คาดหวัง ฉันต้องการทำผ่าน JUnit แต่ฉันไม่แน่ใจว่าจะทำได้อย่างไร

คุณใช้แนวทางใดในการทดสอบบริการบนเว็บของคุณ

อัปเดต: ดังที่ entzik ชี้ให้เห็นการแยกบริการเว็บออกจากตรรกะทางธุรกิจทำให้ฉันสามารถทดสอบตรรกะทางธุรกิจได้ อย่างไรก็ตามฉันต้องการทดสอบรหัสสถานะ HTTP ที่ถูกต้องเป็นต้น


6
คำถามที่ดี - อย่างไรก็ตามฉันจะบอกว่าหากคุณกำลังทดสอบผ่าน HTTP มันทำให้ฉันตกใจว่านี่เป็นการทดสอบการรวม
Tom Duckering

ทอม. คุณมีสิทธิ์อย่างแน่นอน เราควรฉีดตัวจำลอง HTTP จำลอง / คอนเทนเนอร์น้ำหนักเบาสำหรับสิ่งนี้ ใน node.js world supertest ทำให้สิ่งนี้ คุณสามารถเลียนแบบ express.js
FıratKÜÇÜK

คำตอบ:


34

Jerseyมาพร้อมกับ API ไคลเอนต์ RESTful ที่ยอดเยี่ยมซึ่งทำให้การทดสอบหน่วยการเขียนเป็นเรื่องง่ายมาก ดูการทดสอบหน่วยในตัวอย่างที่มาพร้อมกับเจอร์ซีย์ เราใช้แนวทางนี้เพื่อทดสอบการสนับสนุน REST ในApache Camelหากคุณสนใจกรณีทดสอบอยู่ที่นี่


6
re: ตอนนี้ลิงค์ไม่ดีคุณสามารถค้นหาตัวอย่างที่กล่าวถึงในเสื้อ / ตัวอย่างที่แสดงการทดสอบหน่วยโดยทั่วไปโดยใช้ผู้บริโภคของเจอร์ซีเพื่อใช้ทรัพยากรบนเว็บ download.java.net/maven/2/com/sun/jersey/samples/bookstore/…
rogerdpack

2
โครงการนี้อยู่บน GitHub ค้นหาการทดสอบในโฟลเดอร์ src / test: github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat

2
ฉันไม่สงสัยคำตอบนี้ แต่ฉันคิดว่ามันตลกอย่างไม่น่าเชื่อที่เจอร์ซีย์มักจะเข้าสู่การสนทนา JAX-RS เสมอเมื่อในบางกรณี (น่าเสียดายที่ WebSphere น่าจะเป็นที่แน่นอน) มันไม่พร้อมใช้งานและแสดงผล 99% ของคำตอบที่ยอมรับได้ทั้งหมด บน Stack Overflow เป็นโมฆะ

26

คุณสามารถทดลองใช้REST Assuredซึ่งทำให้ง่ายมากในการทดสอบบริการ REST และตรวจสอบการตอบสนองใน Java (โดยใช้ JUnit หรือ TestNG)


1
ฉันโหวตโพสต์ของคุณเพราะห้องสมุดดูดี แต่พวกเขาใช้ขวดโหลที่ขึ้นอยู่กับจำนวนมาก ...
Perry Tew

18

ตามที่เจมส์พูด; มีกรอบการทดสอบในตัวสำหรับ Jersey ตัวอย่างง่ายๆของ hello world สามารถเป็นดังนี้:

pom.xml สำหรับการรวม maven mvn testเมื่อคุณเรียกใช้ เฟรมเวิร์กเริ่มคอนเทนเนอร์กริซลี่ย์ คุณสามารถใช้ท่าเทียบเรือหรือแมวตัวผู้โดยเปลี่ยนการอ้างอิง

...
<dependencies>
  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.16</version>
  </dependency>

  <dependency>
    <groupId>org.glassfish.jersey.test-framework</groupId>
    <artifactId>jersey-test-framework-core</artifactId>
    <version>2.16</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.16</version>
    <scope>test</scope>
  </dependency>
</dependencies>
...

ExampleApp.java

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class ExampleApp extends Application {

}

HelloWorld.java

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public final class HelloWorld {

    @GET
    @Path("/hello")
    @Produces(MediaType.TEXT_PLAIN)
    public String sayHelloWorld() {

        return "Hello World!";
    }
}

HelloWorldTest.java

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.core.Application;
import static org.junit.Assert.assertEquals;

public class HelloWorldTest extends JerseyTest {

    @Test
    public void testSayHello() {

        final String hello = target("hello").request().get(String.class);

        assertEquals("Hello World!", hello);
    }

    @Override
    protected Application configure() {

        return new ResourceConfig(HelloWorld.class);
    }
}

คุณสามารถตรวจสอบนี้โปรแกรมตัวอย่าง


ด้วย Jersey 2.29.1 ฉันต้องเพิ่มjersey-hk2การพึ่งพาเพราะฉันได้รับjava.lang.IllegalStateException: InjectionManagerFactoryข้อผิดพลาดที่ไม่พบ (ดูคำถามนี้ ) มิฉะนั้นตัวอย่างนี้ใช้ได้ดี
Sarah N

7

คุณอาจเขียนโค้ด java ที่ใช้ตรรกะทางธุรกิจของคุณจากนั้นคุณได้สร้างจุดสิ้นสุดของบริการเว็บขึ้นมา

สิ่งสำคัญที่ต้องทำคือทดสอบตรรกะทางธุรกิจของคุณอย่างอิสระ เนื่องจากเป็นรหัส java แท้คุณสามารถทำได้ด้วยการทดสอบ JUnit ปกติ

ตอนนี้เนื่องจากส่วนของบริการบนเว็บเป็นเพียงจุดสิ้นสุดสิ่งที่คุณต้องการตรวจสอบให้แน่ใจก็คือท่อประปาที่สร้างขึ้น (ต้นขั้ว ฯลฯ ) จะซิงค์กับรหัสจาวาของคุณ คุณสามารถทำได้โดยเขียนการทดสอบ JUnit ที่เรียกใช้ไคลเอ็นต์ java บริการเว็บที่สร้างขึ้น สิ่งนี้จะแจ้งให้คุณทราบเมื่อคุณเปลี่ยนลายเซ็น java โดยไม่ต้องอัปเดตสิ่งที่ให้บริการบนเว็บ

หากระบบการสร้างบริการบนเว็บของคุณสร้างขึ้นโดยอัตโนมัติในทุกบิลด์อาจไม่จำเป็นต้องทดสอบจุดสิ้นสุด (สมมติว่าระบบสร้างทั้งหมดถูกสร้างขึ้นอย่างถูกต้อง) ขึ้นอยู่กับระดับความหวาดระแวงของคุณ


2
คุณค่อนข้างถูกต้องแม้ว่าฉันจะต้องทดสอบการตอบสนอง HTTP จริงที่ได้รับกลับมาโดยเฉพาะรหัสสถานะ HTTP
Einar

6

แม้ว่าจะสายเกินไปนับจากวันที่โพสต์คำถาม แต่คิดว่านี่อาจเป็นประโยชน์สำหรับผู้อื่นที่มีคำถามคล้าย ๆ กัน Jersey มาพร้อมกับกรอบการทดสอบที่เรียกว่าJersey Test Frameworkซึ่งช่วยให้คุณสามารถทดสอบ RESTful Web Service ของคุณรวมถึงรหัสสถานะการตอบกลับ คุณสามารถใช้เพื่อทำการทดสอบบนคอนเทนเนอร์น้ำหนักเบาเช่น Grizzly, HTTPServer และ / หรือ EmbeddedGlassFish นอกจากนี้เฟรมเวิร์กยังสามารถใช้เพื่อเรียกใช้การทดสอบของคุณบนเว็บคอนเทนเนอร์ปกติเช่น GlassFish หรือ Tomcat


คุณมีตัวอย่างที่ดีเกี่ยวกับวิธีการจำลองตัวจัดการการโทรหรือไม่ JerseyHttpCall -> MyResource -> CallHandler.getSomething () เราจะเยาะเย้ย CallHandler ได้อย่างไร?
Balaji Boggaram Ramanarayan

3

ฉันใช้HTTPClientของ Apache (http://hc.apache.org/)เพื่อโทรหา Restful Services ไลบรารีไคลเอ็นต์ HTTP ช่วยให้คุณดำเนินการรับโพสต์หรือการดำเนินการอื่น ๆ ที่คุณต้องการได้อย่างง่ายดาย หากบริการของคุณใช้ JAXB สำหรับการรวม xml คุณสามารถสร้าง JAXBContext เพื่อทำให้เป็นอนุกรมและแยกสายอินพุตและเอาต์พุตจากคำขอ HTTP


3

ลองดูที่เครื่องกำเนิดไฟฟ้าส่วนที่เหลือลูกค้าขลัง สิ่งนี้สามารถสร้างการใช้งานพร็อกซีสำหรับคลาสบริการเว็บ JAX-RS ของคุณโดยใช้ไคลเอ็นต์เจอร์ซีย์ที่อยู่เบื้องหลังฉาก อย่างมีประสิทธิภาพคุณจะเรียกคุณว่าวิธีการบริการเว็บเป็นวิธี Java ง่ายๆจากการทดสอบหน่วยของคุณ จัดการการพิสูจน์ตัวตน http ด้วย

ไม่มีการสร้างรหัสที่เกี่ยวข้องหากคุณต้องการเพียงแค่เรียกใช้การทดสอบดังนั้นจึงสะดวก

Dislclaimer: ฉันเป็นผู้เขียนห้องสมุดนี้


2

ง่าย ๆ เข้าไว้. ดูที่https://github.com/valid4j/http-matchersซึ่งสามารถนำเข้าได้จาก Maven Central

    <dependency>
        <groupId>org.valid4j</groupId>
        <artifactId>http-matchers</artifactId>
        <version>1.0</version>
    </dependency>

ตัวอย่างการใช้งาน:

// Statically import the library entry point:
import static org.valid4j.matchers.http.HttpResponseMatchers.*;

// Invoke your web service using plain JAX-RS. E.g:
Client client = ClientBuilder.newClient();
Response response = client.target("http://example.org/hello").request("text/plain").get();

// Verify the response
assertThat(response, hasStatus(Status.OK));
assertThat(response, hasHeader("Content-Encoding", equalTo("gzip")));
assertThat(response, hasEntity(equalTo("content")));
// etc...

1

สิ่งสำคัญที่ต้องทำคือทดสอบตรรกะทางธุรกิจของคุณอย่างอิสระ

แน่นอนฉันจะไม่คิดว่าคนที่เขียนรหัส JAX-RS และต้องการทดสอบหน่วยอินเทอร์เฟซนั้นเป็นอย่างใดด้วยเหตุผลที่แปลกประหลาดอธิบายไม่ได้โดยไม่ต้องกังวลกับความคิดที่ว่าเขาหรือเธอสามารถทดสอบส่วนอื่น ๆ ของโปรแกรมได้ รวมถึงคลาสตรรกะทางธุรกิจ แทบจะไม่มีประโยชน์ที่จะระบุสิ่งที่ชัดเจนและมีการทำประเด็นซ้ำแล้วซ้ำเล่าซึ่งจำเป็นต้องมีการทดสอบการตอบสนองด้วย

ทั้ง Jersey และ RESTEasy มีแอปพลิเคชันไคลเอนต์และในกรณีของ RESTEasy คุณสามารถใช้คำอธิบายประกอบเดียวกันได้ (แม้แต่แยกส่วนต่อประสานที่มีคำอธิบายประกอบออกและใช้กับการทดสอบของคุณในฝั่งไคลเอ็นต์และเซิร์ฟเวอร์)

งดเว้นสิ่งที่บริการนี้สามารถทำเพื่อคุณได้ พักสิ่งที่คุณสามารถทำได้สำหรับบริการนี้


ผู้คนอาจต้องการทดสอบข้อกังวลเกี่ยวกับการตัดข้าม เช่นการตรวจสอบความถูกต้องการรับรองความถูกต้องส่วนหัว HTTP ที่ต้องการเป็นต้นดังนั้นผู้คนจึงชอบทดสอบโค้ด JAX-RS มากกว่า
FıratKÜÇÜK

ในแอปพลิเคชันของฉันฉันใช้ ModelMapper เพื่อ "แมป" จากคลาส "DTO" ไปยังคลาส "อ็อบเจ็กต์ธุรกิจ" ซึ่งคลาส "บริการ" ที่เป็นพื้นฐานเข้าใจได้ นี่คือตัวอย่างของสิ่งที่ดีในการทดสอบโดยอิสระ
jkerak

และบางครั้งแอพเพล็ต REST มีความซับซ้อนเพียงเล็กน้อยจน mocks จะมีขนาดใหญ่กว่าชั้นแอปพลิเคชันเช่นในกรณีปัจจุบันของฉัน :)
tekHedd

1

ตามที่ฉันเข้าใจจุดประสงค์หลักของผู้ตรวจสอบสิทธิ์ของปัญหานี้คือการแยกเลเยอร์ JAX RS ออกจากชั้นธุรกิจ และทดสอบหน่วยเพียงครั้งแรก ปัญหาพื้นฐานสองประการที่เราต้องแก้ไข:

  1. รันทดสอบเว็บ / แอปพลิเคชันเซิร์ฟเวอร์บางตัวใส่ส่วนประกอบ JAX RS เข้าไป และมีเพียงพวกเขา
  2. จำลองบริการธุรกิจภายในส่วนประกอบ JAX RS / เลเยอร์ REST

คนแรกแก้ไขด้วย Arquillian อันที่สองอธิบายได้อย่างสมบูรณ์แบบในอาร์คิลลิกันและเยาะเย้ย

นี่คือตัวอย่างของโค้ดซึ่งอาจแตกต่างกันไปหากคุณใช้แอปพลิเคชันเซิร์ฟเวอร์อื่น แต่ฉันหวังว่าคุณจะได้รับแนวคิดพื้นฐานและข้อดี

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

import com.brandmaker.skinning.service.SomeBean;

/**
* Created by alexandr on 31.07.15.
*/
@Path("/entities")
public class RestBean
{
   @Inject
   SomeBean bean;

   @GET
   public String getEntiry()
   {
       return bean.methodToBeMoked();
   }
}

import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

import com.google.common.collect.Sets;

/**
*/
@ApplicationPath("res")
public class JAXRSConfiguration extends Application
{
   @Override
   public Set<Class<?>> getClasses()
   {
       return Sets.newHashSet(RestBean.class);
   }
}


public class SomeBean
{
   public String methodToBeMoked()
   {
       return "Original";
   }
}

import javax.enterprise.inject.Specializes;

import com.brandmaker.skinning.service.SomeBean;

/**
*/
@Specializes
public class SomeBeanMock extends SomeBean
{
   @Override
   public String methodToBeMoked()
   {
       return "Mocked";
   }
}

@RunWith(Arquillian.class)
public class RestBeanTest
{
   @Deployment
   public static WebArchive createDeployment() {
       WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
               .addClasses(JAXRSConfiguration.class, RestBean.class, SomeBean.class, SomeBeanMock.class)
               .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
       System.out.println(war.toString(true));
       return war;
   }

   @Test
   public void should_create_greeting() {
       Client client = ClientBuilder.newClient();
       WebTarget target = client.target("http://127.0.0.1:8181/test/res/entities");
       //Building the request i.e a GET request to the RESTful Webservice defined
       //by the URI in the WebTarget instance.
       Invocation invocation = target.request().buildGet();
       //Invoking the request to the RESTful API and capturing the Response.
       Response response = invocation.invoke();
       //As we know that this RESTful Webserivce returns the XML data which can be unmarshalled
       //into the instance of Books by using JAXB.
       Assert.assertEquals("Mocked", response.readEntity(String.class));
   }
}

หมายเหตุสองสามข้อ:

  1. การกำหนดค่า JAX RS ที่ไม่มี web.xml ถูกใช้ที่นี่
  2. ที่นี่ใช้ไคลเอ็นต์ JAX RS (ไม่มี RESTEasy / Jersey แต่จะเปิดเผย API ที่สะดวกกว่า)
  3. เมื่อการทดสอบเริ่มขึ้นนักวิ่งของ Arquillian ก็เริ่มทำงาน ที่นี่คุณสามารถค้นหาวิธีกำหนดค่าการทดสอบสำหรับ Arquillian ด้วยแอ็พพลิเคชันเซิร์ฟเวอร์ที่จำเป็น
  4. ขึ้นอยู่กับแอ็พพลิเคชันเซิร์ฟเวอร์ที่เลือก url ในการทดสอบจะแตกต่างกันเล็กน้อย อาจใช้พอร์ตอื่น 8181 ถูกใช้โดย Glassfish Embedded ในตัวอย่างของฉัน

หวังว่ามันจะช่วยได้


สิ่งประเภทนี้ได้รับการสนับสนุนโดยกรอบการทดสอบเสื้อในตัวหรือไม่? ฉันแค่ลังเลที่จะเพิ่ม "อีกหนึ่งเฟรมเวิร์ก" และขวดทั้งหมดในโปรเจ็กต์ของฉัน
jkerak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.