Share
Embed
Transcript
38 slides
-
• > 100 endpoints • ~ 40 team members •
2 mobile platforms + backend • Micro-services architecture • Need for tests Given -
Codeception • PHP sucks • Concurrency support is limited •
API client is low level and verbose • Hard to maintain large test suite • No static typing • Code is not reusable codeception.com -
Runscope • UI Based • Slooooow • Hard to maintain
large test suite • Code is not reusable runscope.com -
Mocha/Supertest/ Chakram • API client is low level and verbose
• Concurrency support is limited • Hard to maintain large test suite • No static typing • Code is not reusable https://mochajs.org https://github.com/visionmedia/supertest http://dareid.github.io/chakram/ -
Requirements • Parallel execution • Simple test structure. No async
in tests • Test should operate with business domain, not with HTTP requests • Work with Objects not JSON and Strings • Static typing • Code reusability -
Java to the rescue! • Already used in the Android
app for UI tests • Can be shared between multiple components • Good concurrency support • Mature test frameworks -
Main components • Test framework • Domain models • API
Client • Support: assertions frameworks, reporting tool, etc -
TestNG • xUnit style • Parallel test execution • Powerful
concurrency management using method and group dependencies • Nice features like data providers testng.org -
Models • Request models • Response models • Assertions for
models attrs., not for JSON/XML attrs. Never work with raw data • Response structure validation should be done by model parser, not by humans -
Immutable Objects More samples See https://github.com/techery/janet-sample-api-integration http://immutables.github.io @Value.Immutable @Gson.TypeAdapters public
interface User { @SerializedName("name") String name(); @SerializedName("id") int id(); } -
REST-Assured • Simple DSL • Designed especially for testing •
POJO support • Might be too low level sometimes • No code reuse :( rest-assured.io -
REST-Assured sample More samples See https://github.com/techery/janet-sample-api-integration given(). param("text", “hey"). param("title",
“Amazing post"). when(). post("/posts"). then(). statusCode(201); -
Retrofit • One of the most popular API frameworks for
Java • Code can be reused outside of test project • Can use POJOs • Not flexible enough sometimes square.github.io/retrofit/ -
Retrofit sample More samples See https://github.com/techery/janet-sample-api-integration public static class BlogPost
{ public int id; public String title; public String text; } public interface BlogAPI { @POST("/posts") BlogPost create(@Field("title") String title, @Field("text") String text); } void testBlogPosting() { String title = "Amazing post"; String body = "hey"; BlogPost blogPost = blogAPI.create(title, body); assertThat(blogPost.text, equals(title)); } -
Janet • Command based service layer • Protocol agnostic •
Based on reactive programming approach • Can be used for HTTP with janet-http service • Can be used in any JVM app github.com/techery/janet -
Janet Action • Java class that describes operation • Each
API request is a separate Janet Action • Purely declarative • Annotations are used to map class attrs. to HTTP request values • Action are executed by services. (HTTPService, WebSocketService, SOAPService, YourCustomService) -
Step 1: define action More samples See https://github.com/techery/janet-sample-api-integration @HttpAction(value =
"/health_check", method = HttpAction.Method.GET) public class HealthCheckAction { @Query("url") public final String url; public HealthCheckAction(String url) { this.url = url; } @Response public HealthStatus response; } -
Step 2: define model More samples See https://github.com/techery/janet-sample-api-integration public class
HealthStatus { @SerializedName("is_available") public Boolean isAvailable; @SerializedName("responseTime") public float responseTime; } -
Step 3: write test More samples See https://github.com/techery/janet-sample-api-integration public class
HealthCheckTest { Janet janet; void testHealthCheck() { String originalURL = "https://techery.io/jobs"; final ActionPipe<HealthCheckAction> pipe = janet.createPipe(HealthCheckAction.class); final Observable<ActionState<HealthCheckAction>> observable = pipe.createObservable(new HealthCheckAction(originalURL)); HealthCheckAction action = observable.toBlocking().last().action; HealthStatus response = action.response; assertThat(response).isNotNull(); assertThat(response.isAvailable).isTrue(); assertThat(response.responseTime).isLessThan(100.0f); } } -
Step 3: write test with some helpers More samples See
https://github.com/techery/janet-sample-api-integration public class HealthCheckTest extends BaseTest { void testHealthCheck() { String originalURL = "https://techery.io/jobs"; HealthCheckAction action = execute(new HealthCheckAction(originalURL)); HealthStatus response = action.response; assertThat(response).isNotNull(); assertThat(response.isAvailable).isTrue(); assertThat(response.responseTime).isLessThan(100.0f); } } -
Step 1: define action More samples See https://github.com/techery/janet-sample-api-integration @WsAction(event =
"health_check") public class HealthCheckAction { @Payload("url") public final String url; public HealthCheckAction(String url) { this.url = url; } @Response public HealthStatus response; } -
Step 2: define model More samples See https://github.com/techery/janet-sample-api-integration public class
HealthStatus { @SerializedName("is_available") public Boolean isAvailable; @SerializedName("responseTime") public float responseTime; } -
Step 3: write test with some helpers More samples See
https://github.com/techery/janet-sample-api-integration public class HealthCheckTest extends BaseTest { void testHealthCheck() { String originalURL = "https://techery.io/jobs"; HealthCheckAction action = execute(new HealthCheckAction(originalURL)); HealthStatus response = action.response; assertThat(response).isNotNull(); assertThat(response.isAvailable).isTrue(); assertThat(response.responseTime).isLessThan(100.0f); } } -
API development workflow 1. New API feature request 2. Team
defines models and actions 3. Team writes tests for new endpoint 4. Android team can now start implementing feature by mocking actions if required. 5. Tests run on CI to check if feature is working 6. Backend team deploys new API endpoint 7. Tests pass and feature is ready -
API development workflow Actions and Models serve as an API
contract and every team member can contribute to it -
Platform structure Janet based API Client Android app API tests
UI tests API docs (in beta) iOS Client (in future) -
Links • https://github.com/techery/janet-sample-api- integration • https://github.com/techery/janet-sample-api- integration • https://github.com/techery/janet-http •
http://testng.org/doc/documentation- main.html • http://immutables.github.io