Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Introduction to Java REST API Testing

DZone's Guide to

Introduction to Java REST API Testing

Learn more about Java REST API testing, from preparing a proper testing suite to correct tools.

Free Resource

Share, secure, distribute, control, and monetize your APIs with the platform built with performance, time-to-value, and growth in mind. Free 90-day trial of 3Scale by Red Hat

There is no doubt that that any piece of software should have automated tests. Tests assure us that by adding new functionality, we are not breaking anything that has been already done. Tests document the software. Tests decrease the amount of manual debugging. After reading a number of articles and books about automated tests, you can actually believe that automated tests are the panacea for all the evil in the software development world. And Java backend microservice application is no different.

Unfortunately, there is no magic in tests. Tests need to be well written to provide any value. Moreover, a misunderstood idea of automated tests can be even worse than having no tests at all, as they will trick you with the false feedback.

How to Prepare a Proper Suite of Tests for a Simple Java Microservice?

First of all, we need to make sure that modules composing our application are working well. Constraints, validation, specific business rules applied to your value objects are to be checked by unit tests. Also, all kinds of serializing, deserializing, marshalling, equals and hashcode contracts need to be tested.

Always Use Proper Tools!

One of the most useful ones is EqualsVerifier. No matter how big the DTO class is, the hashcode and equal contract test are reduced to a couple of lines:

@Test
public void testEqualsAndHashCode() {
  EqualsVerifier
    .forClass(MyClass.class)
    .withCachedHashCode("cachedHashCode", "calculateHashCode", MyClassMother.buildFullObjectBuilder().build())
    .allFieldsShouldBeUsed()
    .verify();
}

The other tip is to use “ObjectMothers.” They are the factory classes which provide you with instances of objects you are using in your tests. The idea behind is to reduce code duplication (imagine you would have to add a new field to MyClass and to update all the tests!) and enhance tests readability by providing descriptive factory method names. (more about: martinfowler.com/bliki/ObjectMother.html)

Also, you might like to check out AssertJ: joel-costigliola.github.io/assertj/ AssertJ ease writing and reading assertions. Your validation test can be as descriptive as:

@Test
 public void shouldValidateCustomerUpdateGroup() throws Exception {
   Set violations = VALIDATOR.validate(CustomerInfo.newBuilder().build(), CustomerUpdateGroup.class);
   assertThat(violations)
     .hasSize(5)
     .extractingResultOf("getPropertyPath", Path.class)
     .extractingResultOf("toString", String.class)
     .contains(
       CustomerInfo.NAME,
       CustomerInfo.CUSTOMER_TYPE,
       CustomerInfo.INVOICE_DATA,
       CustomerInfo.LANGUAGE,
       CustomerInfo.STATUS);
 }

Keep in Mind That Tools Are Not Everything

Remember to check more than a happy path.

Take a look at this little method and pretend that getting those distinct numbers takes a lot of complicated code.

public List findUniques(List numbers){
  return numbers.stream().distinct().collect(Collectors.toList());
 }

Can you think of test scenarios?

You should check:

  • some duplicates like 1,1,2,4,4,10
  • non-duplicated 1, 2, 3
  • empty array
  • and of course: null

Be sure to use your IDE code coverage plugin to make sure that you did not omit anything!

Having separated modules covered by unit tests, it’s time to move towards integration testing. Those are very convenient because they actually describe what the system does. Moreover, short tests with descriptive names are the valuable source of knowledge for a developer who has just joined the project. If the code has all the details, the integration tests can be treated as bullet points, giving a brief description of what the code should do.

However, for the best effect, some preparation must be made: we need to hide all the plumbing to preserve clarity. In Neoteric we are providing a base class with protected instances of everything needed, the basic idea looks similar to:

public class IntegrationTestBase {
  protected static DB dbInstance;
  protected static Server httpServer;
  //….
  protected static DBCollection customersCollection;

  @BeforeClass
  public static void setUpTestFrameworks(){
    dbInstance = DBFactory.newInstance();
    // ….
  }

  @Before
  public static void setUpTestCollections(){
    customersCollection = dbInstance.getCollection(“customers”);
  }

  @After
  public static void dropTestCollections(){
    customersCollection.drop();
  }

  @AfterClass
  public static shutDownFrameworks()...
}

All we need to add are some utility methods and classes to map responses, generate test objects, and we are good to go!

We will cover this by example. We want to check if our application will serve a JSON with a customer from customers’ collection when an HTTP GET request is invoked on a “/api/customers/{id}” path. The basic tool is Jayways rest assured (you definitely need to take a look at: github.com/jayway/rest-assured )
Let’s take a quick look:

public class CustomersApiTest extends IntegrationTestBase {

  @Test
  public void shouldGetSingleCustomer(){
    Customer customerDb = customersCollection.insert(CustomerMother.regular());
    String response = given().header(........)
      .when()
      .get(“api/customers/” + customerDb.getId())
      .then()
      .statusCode(HttpStatus.OK_200)
      .extract()
      .asString();

    assertThat(getObjectFromResponse(response)).isEqualTo(customerDb);
  }
}

That’s it! You’ve just made sure that the data from database successfully passed all the tiers of your application, from byte representation in DB to a JSON ready for consumption by a client.

Remember that the happy path is deceiving. To make sure that your endpoint is working properly you need to check how it behaves when a malformed or non-existing customer id is requested, what happens when a user is not authorised to such request and other examples specific to your application.

Remember also that Robert Martin wants you to keep your tests as clean as production code – test are first class citizens of your application!

/Added by Claudia from Neoteric, written and authorized by Krzysztof Miotk

Explore the core elements of owning an API strategy and best practices for effective API programs. Download the API Owner's Manual, brought to you by 3Scale by Red Hat

Topics:
java ,api ,rest api ,testing

Published at DZone with permission of Claudia from Neoteric. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}