unit-testing - Clojure.spec:基于生成器中其他字段的字段存在
问题描述
假设我们有 API 可以将不同类型的文件属性保存到 DB* 中:文本、图像、音频和视频文件。它应该能够根据它们的类型获取以下字段:
所有文件的基本属性:
{"file-type": "text",
"location": "/Documents",
"creation-time": "2020-03-02",
"name": "sometext.txt"}
仅针对某些类型的附加道具:
text: only base props
video file: base props + {"duration(s)":123, "resolution":"4k"}
audio file: base props + {"duration(s)":123}
image: base props + {"resolution":"2048x1536"}
正如我们所看到的,某些字段应该 nil 依赖于“文件类型”。假设我们需要验证这种输入,它可以是所描述的任何类型。所以规格是:
(s/def ::file-type (s/with-gen (s/and string? not-empty) #(s/gen #{"text" "image" "video" "audio"})))
(s/def ::location (s/with-gen (s/and string? not-empty) #(s/gen #{"/Documents"})))
(s/def ::creation-time (s/with-gen (s/and string? not-empty) #(s/gen #{"2020-03-02"}))) ;for simplicity
(s/def ::name (s/with-gen (s/and string? not-empty) #(s/gen #{"sometext.txt" "image.jpg" "video.mp4" "audio.mp3"}))) ;for simplicity
(s/def ::duration (s/and int? not-empty)) ;for simplicity
(s/def ::resolution (s/with-gen (s/and string? not-empty) #(s/gen #{"4k" "2048x1536"}))) ;for simplicity
(s/def ::files-input (s/keys :req-un [::file-type ::location ::creation-time ::extension] :opt-un [::duration ::resolution]))
还假设有一个程序验证器检查是否为每种文件类型传递了正确的字段集(例如,“video”有“duration”字段,但“text”没有)。
问题是:如何为单元测试(包括依赖字段) 生成具有这些依赖项的完整现成输入?
*(让我们抛开它是否是 API 的正确设计的问题,因为这个例子不是来自现实生活并且仅用于演示目的)
解决方案
基于multi-spec doc,我们必须为每种情况添加具有自己的字段集的单独方法:
(s/def ::file-type (s/with-gen (s/and string? not-empty) #(s/gen #{"text" "image" "video" "audio"})))
(s/def ::location (s/with-gen (s/and string? not-empty) #(s/gen #{"/Documents"})))
(s/def ::creation-time (s/with-gen (s/and string? not-empty) #(s/gen #{"2020-03-02"}))) ;for simplicity
(s/def ::name (s/with-gen (s/and string? not-empty) #(s/gen #{"sometext.txt" "image.jpg" "video.mp4" "audio.mp3"}))) ;for simplicity
(s/def ::duration pos-int?) ;for simplicity
(s/def ::resolution (s/with-gen (s/and string? not-empty) #(s/gen #{"4k" "2048x1536"}))) ;for simplicity
(s/def ::base-props (s/keys :req-un [::file-type ::location ::creation-time ::name]))
(s/def ::file-type-key (s/with-gen keyword? #(s/gen #{:text :image :video :audio})))
(defmulti file :file-type-key)
(defmethod file :text [_]
(s/merge (s/keys :req-un [::file-type-key]) ::base-props))
(defmethod file :image [_]
(s/merge (s/keys :req-un [::file-type-key ::resolution]) ::base-props))
(defmethod file :video [_]
(s/merge (s/keys :req-un [::file-type-key ::duration ::resolution]) ::base-props))
(defmethod file :audio [_]
(s/merge (s/keys :req-un [::file-type-key ::duration]) ::base-props))
(defmethod file :default [_]
(s/merge (s/keys :req-un [::file-type-key]) ::base-props))
(s/def ::file-input (s/and (s/multi-spec file :file-type-key)
(fn [{:keys [file-type file-type-key]}]
(= file-type-key (keyword file-type)))))
将为单元测试提供生成的输入(可通过检查stest/check
):
(gen/sample (s/gen ::file-input) 2)
=>
({:file-type-key :text, :file-type "text", :location "/Documents", :creation-time "2020-03-02", :name "sometext.txt"}
{:file-type-key :image,
:resolution "4k",
:file-type "image",
:location "/Documents",
:creation-time "2020-03-02",
:name "sometext.txt"})
我们必须再添加一个字段::file-type-key
作为选择器(必须是关键字),但它不会影响测试并且可以dissoc
轻松编辑。
推荐阅读
- python - 我在 Python 文件夹站点包中添加了 jar 文件,运行时在 IDE 中看不到导入的库
- python - 无法将 slack 中的用户名打印为“@John”,以及如何在不手动输入 cntrl+c 的情况下关闭 linux 中的 python 文件
- c++ - 函数根据相同的输入重载和返回不同的数据类型
- angular6 - 在 Angular 6 HttpInterceptor 中抛出异常
- html - 如何使用 Stripe Checkout 处理 POST 请求
- tensorflow - 为什么 tensorflow 会话和图形如此难以理解?
- python - 在 Django 中使用 RegexValidator 验证数字或数字范围的高效方法
- pywinauto - 我们如何获得 WebEx 共享屏幕窗口的 flyOut 菜单元素?
- c# - 了解 Mediatr 的 Autofac 配置
- php - 将控制器中的数据显示为列和行