Skyramp Feature Highlight: Using Dynamic Python for Tests and Mocks
Dan Gross
Posted on October 3, 2023
Python developers, rejoice! Skyramp now supports Python code for dynamically testing and mocking your distributed applications. Previously, Skyramp supported only JavaScript for dynamic requests and responses within test and mock description files. In this blog post, we'll walk you through Skyramp's new capability of employing dynamic Python code in requests and responses for testing and mocking microservices, right from a running Kubernetes cluster.
Unpacking Skyramp Tests
Before diving into the step-by-step example, let's first lay the foundation by understanding what constitutes a Skyramp test. At the heart of every Skyramp test is a test description file. This file can either be auto-generated using skyramp tester generate
or created manually as a .yaml
file.
What's Inside a Test Description?
Within a Skyramp test description file, you define the essential components of your test. This includes the scenarios for testing, the services to be tested, the requests you'll make to them, the expected responses in the form of asserts, and any custom logic or handlers to process these interactions. Let's dissect the important elements.
Here's a simple scenario in a test description:
scenarios:
- name: scenario1
steps:
- request: request1
- asserts: requests.request1.res.message == "abc"
The scenario above simply tests if the message returned from a service is equal to "abc". Below is an example request to support the scenario above. We can assume this hypothetical service takes an id
and returns a message
. Both of these YAML sections are contained within a Skyramp test description file.
requests:
- name: request1
endpoint: our-service
method: GetMessage
requestValue:
blob: |
{
"id": "123"
}
Notice the block under requestValue
. It is configured as a static blob
in this example, but it can also be configured as dynamic code that the Skyramp Worker will execute during a test. The language used in a dynamic handler had been limited to JavaScript, but now you can use Python! Let's look at how.
A Hands-on Example: Testing with Python Code
To illustrate how Python can now be harnessed in Skyramp tests, let's embark on a simple journey you can follow yourself. We'll clone a sample project from a Skyramp-provided repo on GitHub, which conveniently contains microservices that can be swiftly deployed to a Kubernetes cluster, all primed and ready for testing.
Paving the Way with the Skyramp CLI
First, make sure you have the Skyramp CLI installed. The Skyramp CLI enables the Deployer, Mocker, and Tester tools. You can follow these easy steps from the Skyramp Docs for installation.
Cloning the Project
Start with cloning Skyramp's letsramp/sample-microservices
repo, which is a sample e-commerce system that includes a number of microservices. See the GitHub repo Readme for additional information. The Skyramp repo is based on Google Cloud's Online Boutique, adding test demos and support for REST and Thrift APIs.
From a terminal, you can execute this command:
git clone https://github.com/letsramp/sample-microservices.git
Start your Cluster
Now that the project is in place, you can navigate to the grpc-demo
directory in your terminal and bring up a local cluster for testing by executing these three commands:
cd sample-microservices/skyramp/grpc-demo
skyramp cluster create --local
skyramp deployer up checkout-system
Putting Python to the Test
With the system-under-test up and running, let's edit one of the tests to show where we can insert Python code that will be run dynamically during testing. The tests/checkout-test.yaml
test description file contains a requests
section with a static blob. Edit the section under requestValue
for request1
as follows:
requests:
- name: request1
endpoint: cart-service
method: AddItem
requestValue:
python: |
def handler():
uid = "abcde"
qty = 5 * 5
pid = "OLJCE"
pid += "SPC7Z"
request_value = {
"userId": uid,
"item": {
"productId": pid,
"quantity": qty
}
}
return SkyrampValue(value=request_value)
You can see we replaced the blob
attribute with python
and are now creating the request values in code. To learn more about the specific details of how you can use Python in test descriptions, visit the Skyramp Docs that cover Python Dynamic Requests.
You will notice that the tests/checkout-test.yaml
test description file has two requests. The first request, shown above as request1
, adds an item to the cart. The second request, request2
, places an order. We will leave the requestValue
in request2
as a static blob
.
For our test scenario under scenarios
, we first make a request to request1
and then make a request to request2
. The existing assert checks that the productId
matches the first item returned in the response from request2
. Let's also add an asserts
line in the same file to check the quantity
value returned. This should match the quantity
we set in request1
. Notice the type is a number
and not a string
.
scenarios:
- name: scenario1
steps:
- request: request1
- request: request2
- asserts: requests.request2.res.order.items[0].item.productId == "OLJCESPC7Z"
- asserts: requests.request2.res.order.items[0].item.quantity == 25
We are almost ready to run our new test scenario with Python, but we are missing one final piece. We know our checkout depends on the payment service, but it is not running in our system-under-test. Luckily, we have a mock for that.
Yes, Mocks Too!
Not only is Python available for dynamic requests in the test description, but Python is also available for dynamic responses in the mock description.
Open mocks/payment-service-k8s.yaml
for editing, which is also under the skyramp/grpc-demo
folder in the letsramp/sample-microservices
repo.
Change the section under responseValues
to include the python
attribute and the code shown below:
responseValues:
- name: ChargeResponse
python: |
def handler(req):
key = "transaction_id"
value = "default-string"
return SkyrampValue(value={key: value})
Here, we now return values from Python-generated variables instead of a static blob
as before. To learn more about the specific details of how you can use Python in mock descriptions, visit the Skyramp Docs that cover
Python Dynamic Responses
Mocking and Testing with Skyramp
With our new test description and mock description updated to include dynamic Python, we can proceed with our test session. Skyramp makes running tests a breeze.
First, apply the mock that was updated with Python code:
skyramp mocker apply -n test-grpc-demo
You should see this output in the terminal:
Applying mock config [##############################################################] 100 %
Successfully applied mock configurations.
Next, run the test that was updated with Python code:
skyramp tester start checkout-test -n test-grpc-demo
The test results should produce the following output:
- pattern0.scenario1.2.assert
[Status: finished] [Started at: N/A]
Assert: requests.request2.res.order.items[0].item.productId == "OLJCESPC7Z"
Passed: true
- pattern0.scenario1.3.assert
[Status: finished] [Started at: N/A]
Assert: requests.request2.res.order.items[0].item.quantity == 25
Passed: true
Nice job, you did it! This was just a simple taste to show how to use Python for tests and mocks, but there is much more to explore. Additional capabilities include:
- Utilizing the
req
parameter for incoming requests (see docs) - Using the
pythonPath
attribute for external source code files (see docs) - Installing NPM-based packages
- Chaining and Overrides
Wrapping Up
That concludes our exploration of using Python code in Skyramp tests and mocks. Now Python developers can leverage their skills to create comprehensive and robust testing scenarios. We hope you've enjoyed this glimpse into using Python for dynamic requests and responses in testing distributed applications with Skyramp.
Stay tuned for more updates and enhancements as Skyramp continues to evolve, enabling developers to optimize their testing processes like never before. Happy testing!
Posted on October 3, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.