keploy
keploy copied to clipboard
[GSoC]: test multiple services with keploy
Is there an existing feature request for this?
- [X] I have searched the existing issues
Summary
- Use keploy to record tests and mocks for multiple interconnected services.
- And observe the changes in keploy's behavior if there is a major update in one of the services and its impact on new test environments.
Why should this be worked on?
- Since Most of the applications consist of multiple microservices, keploy should be able to test all the services together.
Repository
keploy
Use keploy to record tests and mocks for multiple interconnected services:
I made two spring boot interconnected microservices which are product and order
Models:
Order
```
package com.example.order.models;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Order {
private Long id;
private Long productId;
private int quantity;
}
```
Product
```
package com.example.order.models;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Product {
private Long id;
private String name;
private double price;
}
```
Order Controller:
```
package com.example.order.controllers;
import com.example.order.models.Order;
import com.example.order.models.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@PostMapping
public ResponseEntity placeOrder(@RequestBody Order order) {
ResponseEntity responseEntity = restTemplate.getForEntity(
"http://localhost:8084/products/{productId}", Product.class, order.getProductId());
if (responseEntity.getStatusCode() == HttpStatus.OK) {
Product product = responseEntity.getBody();
return ResponseEntity.ok("Order placed for product: " + product.getName() +
", Quantity: " + order.getQuantity() +
", Total Price: " + (product.getPrice() * order.getQuantity()));
} else {
return ResponseEntity.status(responseEntity.getStatusCode()).body("Failed to place order");
}
}
}
```
Product Controller:
```
package com.example.product.controllers;
import com.example.product.models.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/products")
public class ProductController {
@GetMapping("/{productId}")
public ResponseEntity getProduct(@PathVariable Long productId) {
// In a real scenario, you might fetch product details from a database
Product product = new Product();
product.setId(productId);
product.setName("Sample Product");
product.setPrice(19.99);
return ResponseEntity.ok(product);
}
}
```
Sending order request using curl:
```
curl --location 'http://localhost:8085/orders' \
--header 'Content-Type: application/json' \
--data '{
"id":101,
"productId":105,
"quantity":5
}'
```
While recording the request of order microservice by Keploy:
Test case is generated successfully:
```
version: api.keploy.io/v1beta1
kind: Http
name: test-1
spec:
metadata: {}
req:
method: POST
proto_major: 1
proto_minor: 1
url: http://localhost:8085/orders
header:
Accept: '*/*'
Content-Length: "55"
Content-Type: application/json
Host: localhost:8085
User-Agent: curl/7.68.0
body: |-
{
"id":101,
"productId":105,
"quantity":5
}
body_type: ""
timestamp: 2024-02-14T11:14:59.875844811+02:00
host: ""
resp:
status_code: 200
header:
Content-Length: "85"
Content-Type: text/plain;charset=UTF-8
Date: Wed, 14 Feb 2024 09:15:00 GMT
body: 'Order placed for product: Sample Product, Quantity: 5, Total Price: 99.94999999999999'
body_type: ""
status_message: ""
proto_major: 0
proto_minor: 0
timestamp: 2024-02-14T11:15:02.872504633+02:00
objects: []
assertions:
noise:
header.Date: []
created: 1707902102
curl: |-
curl --request POST \
--url http://localhost:8085/orders \
--header 'Host: localhost:8085' \
--header 'User-Agent: curl/7.68.0' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--data '{
"id":101,
"productId":105,
"quantity":5
}'
```
Can you explain more about "a major update in one of the services"?
@AhmedLotfy02 How did you start the services using keploy? Because I can see only one test case being generated but it should have been 2, one for each service.
Can you explain more about "a major update in one of the services"?
By this, I mean that if there is some change in the API of the second service, how will it affect the other service?
@gouravkrosx I started order service by keploy and the second one normally. but now when i started the second one with keploy one test case is generated for the second service and one for the first service also after requesting the order controller. (two different terminals)
in the first service:
the first test case in this screenshot is related to the above test ignore it)
in the second service:
ok according to
By this, I mean that if there is some change in the API of the second service, how will it affect the other service?
if I understood what you mean making a major changes in the second service which is product service like returning a string rather than product it will cause an error as it expecting that product service will return a product:
the return from order service:
also Keploy will record these test cases:
test case generated in order service:
test case generated in product service:
@AhmedLotfy02 ,
By this, I mean that if there is some change in the API of the second service, how will it affect the other service?
I meant after recording a few test cases, if you make some changes in the Product service & run both the services in test
mode, what will be the behavior? for both services.
Do you think the behavior is valid or not?
I added the code with the PR linked to this issue
After recording a testcase then change one field of the returned object in product service then record then test the both services:
@AhmedLotfy02 This is the actual problem I wanted to see even if there is some change in one service (s2), it is not reflected for the other service(s1) which should not be the case here. Let's say the change in other service(s2) is legit then you'll just update the test case. But the problem is that the mocks will not be updated for the other service(s1) which can break the application or you can say contract.
So, The main GSOC issue is that keploy should be able to support this that mocks should be updated every time there is such change in a different service.
Since the test fail to capture the fact that a consumer service is using an outdating mocks right now, to make things work, when making changes (changing the contract) on a service, we have to update tests file in the provider service and the mocks file in the consumer service, which requires too much manual effort. Adding a contract that allows access for both the provider and the consumer might solve this problem. It can acts as stubs for consumer and normal test cases for provider, which ensure synchronization. I am thinking of where to store these shared contract... It should not be in codebase of microservices or in the cloud.
@gouravkrosx I've implemented it. Please take a look when you have time. https://github.com/seipan/multiple-services-go
This was related to the GSoC qualification task, since the proposal period has been closed, I am closing the issue. Thanks everyone for the contribution.