testingrustproperty-based-testingproptest

How to create JSON object strategy according to a schema with rust proptest?


I'd like to create a JSON strategy using rust proptest library. However, I do not want to create an arbitrary JSON. I'd like to create it according to a schema (more specifically, OpenAPI schema). This means that keys of the JSON are known and I do not want to create them using any strategy, but I'd like to create the values using the strategy (pretty-much recursively).

I already implemented the strategy for primitive types, but I do not how to create a JSON object strategy.

I would like the strategy to have the type BoxedStratedy<serde_json::Value> or be able to map the strategy to this type because the JSON objects can contain other objects, and thus I need to be able to compose the strategies.

I found a HashMapStrategy strategy, however, it can be only created by a hash_map function that takes two strategies - one for generating keys and one for values. I thought that I could use Just strategy for the keys, but it did not lead anywhere. Maybe prop_filter_map could be used.

Here is the code. There are tests too. One is passing because it tests only primitive type and the other is failing since I did not find a way to implement generate_json_object function.

I tried this but the types do not match. Instead of a strategy of map from string to JSON value, it is a strategy of a map from string to BoxedStrategy.

fn generate_json_object(object: &ObjectType) -> BoxedStrategy<serde_json::Value> {
    let mut json_object = serde_json::Map::with_capacity(object.properties.len());
    for (name, schema) in &object.properties {
        let schema_kind = &schema.to_item_ref().schema_kind;
        json_object.insert(name.clone(), schema_kind_to_json(schema_kind));
    }
    Just(serde_json::Value::Object(json_object)).boxed()
}

Solution

  • One can create a vector of strategies, which implements a Strategy trait and can be boxed. So to create a serde_json::Value::Object, we create a vector of tuples. The first element will be a Just of key and the second element will be a boxed strategy of value. The boxed strategy of value can be created by schema_kind_to_json function. After we have a vector of tuples which implement a Strategy, we can use .prop_map to transform it to a serde_json::Value::Object.

    fn generate_json_object(object: &ObjectType) -> BoxedStrategy<serde_json::Value> {
        let mut vec = Vec::with_capacity(object.properties.len());
        for (name, schema) in &object.properties {
            let schema_kind = &schema.to_item_ref().schema_kind;
            vec.push((Just(name.clone()), schema_kind_to_json(schema_kind)));
        }
        vec.prop_map(|vec| serde_json::Value::Object(serde_json::Map::from_iter(vec)))
            .boxed()
    }