Integration Testing
You're reading Part One of the Create Wraps tutorial, where we learn everything you need to know to productively develop Polywrap wraps.
We're going to write some integration tests for our wrap to make sure our wrap works as expected in an environment that resembles the one in which it will be used.
Why integration tests?
Here we will define an "integration test" as an end-to-end tests that validates the functionality of your wrap by invoking it from a Polywrap Client. This is in contrast to a "unit test", which you might use to test the functionality of specific wrap methods in isolation.
We write integration tests because certain wrap functionalities depend on the Polywrap Client. The most obvious example is subinvocation. When a wrap invokes a method in another wrap, the method arguments are serialized, sent back to the Polywrap Client, and routed to the other wrap. The Polywrap Client is required to coordinate the interaction.
A downside of integration testing is that we don't have Polywrap Client for every programming language for which we can write wraps. If you're writing your wrap in Go or AssemblyScript, we assume you'll be okay with testing using the TypeScript Polywrap client.
We also support a language-agnostic approach to integration testing that we call Workflows. Workflows are a way to define a sequence of steps that can be executed by any Polywrap Client. The output of workflow jobs can be validated to ensure that the wrap is functioning as expected. We won't be using workflows in this tutorial.
App Codegen
Codegen will generate different bindings for different types of projects. For integration tests, we use the codegen targeting application developers because they make testing easier and more closely replicate the wrap's end user experience. App codegen is not required--a Polywrap Client can invoke any wrap without codegen--but it's recommended.
To execute app codegen without changing our polywrap.yaml
project manifest or wrap schema, we can simply create another manifest and schema for the purpose of testing and pass them into the codegen command. I changed the namespace of my wrap import in the test schema to "Oracle" so I can refer to it as Oracle
in my tests.
- Rust
- Go
- TypeScript
- AssemblyScript
polywrap codegen -m ./tests/types/polywrap.app.yaml -g ./tests/types/wrap
polywrap codegen -m ./module/__tests__/types/polywrap.app.yaml -g ./module/__tests__/types/wrap
polywrap codegen -m ./src/__tests__/types/polywrap.app.yaml -g ./src/__tests__/types/wrap
polywrap codegen -m ./src/__tests__/types/polywrap.app.yaml -g ./src/__tests__/types/wrap
Write Tests
With app codegen, integration tests are easy to write.
For Rust wraps, we'll write our integration tests in Rust using the Rust Polywrap Client. For wraps written in other languages, we'll write our tests in TypeScript.
We'll invoke our wrap using a file-system Wrap URI.
In your test file, you can import the module class generated with app codegen. We'll use the default Polywrap Client configuration, so we won't need to pass one in as an argument to the module constructor. The module instance can be used to invoke the methods defined in your wrap schema with type safety.
You can use my personal API key to test your wrap.
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
Please do not use it outside the context of this tutorial. If it's abused, I'll have to revoke it!
- Rust
- TypeScript
use crate::types::wrap::types::{
Oracle,
OracleArgsObscure,
OracleArgsEnlighten
};
use std::env;
#[test]
fn obscure() {
let args = OracleArgsObscure {
data: vec![String::from("Hello"), String::from("World")],
chaos_level: Some(3)
};
let oracle: Oracle = Oracle::new(None);
let result = oracle.obscure(&args, None).unwrap();
assert_eq!(result.len(), 128);
}
#[test]
fn enlighten() {
let args = OracleArgsEnlighten {
question: String::from("What is the meaning of life?"),
api_key: env::var("PPLX_API_KEY").expect("API_KEY must be set")
};
let oracle: Oracle = Oracle::new(None);
let result = oracle.enlighten(&args, None).unwrap();
assert_ne!(result.len(), 0);
}
import * as App from "../types/wrap";
import path from "path";
jest.setTimeout(60000);
describe("Oracle Wrap End to End Tests", () => {
let oracle: App.Oracle;
let wrapperUri: string;
let apiKey: string = process.env.PPLX_API_KEY || "";
beforeAll(() => {
const wrapperPath: string = path.join(__dirname, "..", "..", "..");
wrapperUri = `fs/${wrapperPath}/build`;
oracle = new App.Oracle(undefined, undefined, wrapperUri)
})
it("calls obscure", async () => {
const result = await oracle.obscure({
data: ["Hello", "World"],
chaosLevel: 3
});
if (!result.ok) throw result.error;
expect(typeof result.value === "string").toBeTruthy();
expect(result.value.length).toEqual(128);
});
it("calls enlighten", async () => {
const question = "What is the meaning of life?";
const result = await oracle.enlighten({ question, apiKey });
if (!result.ok) throw result.error;
expect(typeof result.value === "string").toBeTruthy();
});
});
Next Steps
In the final part of the tutorial, we'll learn how to publish a wrap.