单元测试生成

在使用图标-插件工具成功生成单元测试之前,必须执行以下操作:

  • 创建格式正确的plugin.spec.yaml文件。
  • 生成插件骨架。
  • 创建带有有效值的示例JSON文件,并将它们存储在/测试目录中。

快速启动

可以基于中定义的操作和触发器生成单元测试plugin.spec.yaml. 单元测试放在/单元测试插件根目录下的子文件夹。

单元测试生成器为每个触发器和动作创建集成测试和单元测试。集成测试将建立“实时”连接(如果插件中可用),并从动作或触发器返回真实数据。单元测试将是一个框架,用于测试动作和触发器的逻辑。

要为您的插件生成单元测试,请打开命令行并进入插件的根目录。

在根目录下运行:

图标插件生成单元测试

运行单元测试

执行以下操作之一:使用所选的IDE(VS代码和Pycharm具有右键单击选项以允许您运行单元测试)

在单元测试目录中运行以下命令:

python3-m单元测试*

第一次测试将失败,但将生成错误消息,为下一步提供指导。单元测试本身也有注释块来帮助您。

要运行一个特定的单元测试:

在“单元测试”目录中运行以下命令:

Python3 -m unittest test_my_action.py

单元测试模板

可以生成两种类型的单元测试:操作单元测试和触发单元测试。

下面几节提供这两个模板的高级概述。

注意:您可能会发现一些测试不适合这些模板(util函数就是一个很好的例子)。您可以在unit_test目录中创建更多的单元测试文件。

此外,如果需要,可以将测试数据分离到自己的文件中。这使得使用curl或Postman的有效负载作为测试数据变得更加容易。与从样例目录读入测试数据的方式类似,您可以从外部文件读入测试数据,以便在测试中使用真正的API有效负载。

单元测试动作模板

在cor中,生成的动作单元测试将如下所示:

         
蟒蛇
1
<进口>
2
TestActionName(测试用例):
deftest_integration_action_name(自我):
4
5
deftest_action_name(自我):

这个框架的目的是帮助您快速使用集成测试。然后,您可以使用集成测试的结果来帮助您生成进一步的单元测试,以测试操作中的逻辑。

行动集成测试:操作集成测试如下所示:

         
蟒蛇
1
日志=日志记录.getlog(“测试”)
2
test_conn=连接()
测试动作={{.ActionClassName}}()
4
5
test_conn.日志记录器=日志
6
测试动作.日志记录器=日志
7
8
试一试:
9
具有开放(“. . /测试/ {{.ActionName}} . json”)作为文件:
10
test_json=json.加载(文件.()).得到(“身体”)
11
connection_params=test_json.得到(“连接”)
12
action_params=test_json.得到(“输入”)
13
除了异常作为e:
14
<消息...>
15
自我.失败(消息)
16
17
18
test_conn.连接(connection_params)
19
测试动作.连接=test_conn
20.
结果=测试动作.运行(action_params)
21
22
#TODO:删除此行
23
自我.失败(“未执行的测试用例”)
24
25
# TODO:下面的断言应该被更新,以从你的操作中寻找数据
26
#例如:self。资产质量({"success": True}, results)
27
自我.资产质量({},结果)

此测试的前几行设置了action类和connection类。然后,测试从相应的示例文件中读取操作参数和连接设置。然后,我们将测试连接设置为action类中的连接。最后,使用其参数运行操作并返回结果。

作为单元测试开发人员,您需要删除self.fail()排队,然后self.assertEquals验证操作输出的合理断言。

在这一点上非常有用的是一个IDE,在这个IDE中,你可以中断test_action.run()函数并探索插件中的数据。(作者注:这里PyCharm是我们Python插件的首选编辑器,然而VS Code在这里做得很好。如果你在命令行中执行所有这些操作,你可以使用print语句来遵循插件的逻辑。)

行动单元测试(s):动作单元测试是未实现测试用例的简单框架。

         
蟒蛇
1
def试验_{{.ActionName}}(自我):
2
自我.失败(“未执行的测试用例”)

在这里,您需要创建测试操作逻辑的单元测试。下面是一个来自Microsoft Teams插件的真实单元测试示例:

         
蟒蛇
1
def测试\编译\消息\内容(自我):
2
核磁共振=NewMessageReceived()
正则表达式=核磁共振.编译消息内容(".")
4
自我.资产真实(正则表达式.搜索(“东西”))
5
6
具有自我.assertRaises(PluginException):
7
核磁共振.编译消息内容(“(”)

这个测试所做的是实例化我们的动作对象newmessagerreceived(),然后测试它的一个函数,在本例中是' compile_message_content '。然后我们断言该函数的输出,以验证它是否按照预期工作。

需要注意的是,这是一个糟糕的单元测试,因为我在一个测试中同时测试了阴性和阳性结果。尽量不要那样做。我想用自我展示。assertRaises’ functionality as that’s a slick way to do negative unit tests.

单元测试触发模板

触发器单元测试比操作测试更复杂。原因是触发器通常在无限循环中运行,只有在满足特定条件时才向协调器发送信息。由于没有“返回”值供我们断言,这些测试更难于编写。

         
蟒蛇
1
<进口…>
2
unittest.模拟进口补丁
进口超时装饰器
4
5
6
#这将捕获超时错误并返回None。这将告诉测试框架我们的测试通过了。
7
#这是必需的,因为触发器中的run函数是一个无止境的循环。
8
deftimeout_pass(函数):
9
deffunc_包装器(*args,**关键字参数):
10
试一试:
11
返回函数(*args,**关键字参数)
12
除了超时装饰器.超时装饰器.时间误差作为e:
13
打印(f“测试按预期超时:{e}")
14
返回没有一个
15
16
返回func_包装器
17
18
#这是对komand.Trigger.send的嘲弄
19
#我们需要这个来欺骗插件,让插件认为它在发送触发器的run函数的输出
20.
假货():
21
def发送(params):
22
打印(params)
23
24
#测试类
25
试验{{.ActionClassName}}(测试用例):
26
27
@超车超时
28
@超时装饰器.超时(30.)
29
@patch(“komand.Trigger.send”,side_effect=假货.发送)
30.
deftest_integration_{{.ActionName}}(自我,mockSend):
31
日志=日志记录.getlog(“测试”)
32
33
试一试:
34
具有开放(“. . /测试/ {{.ActionName}} . json”)作为f:
35
数据=json.负载(f)
36
connection_params=数据.得到(“身体”).得到(“连接”)
37
trigger_params=数据.得到(“身体”).得到(“输入”)
38
除了异常作为e:
39
<消息…>
40
自我.失败(消息)
41
42
test_connection=连接()
43
test_connection.日志记录器=日志
44
test_connection.连接(connection_params)
45
46
test_email_received={{.ActionClassName}}()
47
test_email_received.连接=test_connection
48
test_email_received.日志记录器=日志
49
50
test_email_received.运行(trigger_params)
51
52
自我.失败()#如果我们走到这里,运行循环不知何故失败了
53
54
def试验_{{.ActionName}}_some_function_to_test(自我):
55
自我.失败(“未实施的测试”)

一般来说,它分解如下:

  • 进口货物
  • 假的东西
  • 创建我们的测试类
  • 创建集成触发测试
  • 为其余测试设置骨架

我在上面留下了一些导入行,因为它们很重要。timeout_decorator允许我们超时测试用例。补丁导入允许我们对函数进行正式的模拟。

模板中的打开函数覆盖超时行为,并为komand.Trigger.send创建模拟。

触发器集成测试如下所示:

         
1
@超车超时
2
@timeout_decorator.timeout (30)
@补丁(“komand.Trigger.send”,副作用=fakeSender.send)
4
def test_integration_{{。ActionName}}(自我,mockSend):
5
log=logging.getLogger(“测试”)
6
7
试一试:
8
使用open("../tests/{{.ActionName}}.json")作为f:
9
data = json.load (f)
10
connection_params = data.get(“身体”). get(“连接”)
11
trigger_params = data.get(“身体”). get(“输入”)
12
例外情况除外,如e:
13
14
self.fail(消息)
15
16
test_connection =连接()
17
test_connection.logger=log
18
test_connection.connect (connection_params)
19
20.
test_email_received = {{.ActionClassName}} ()
21
test_email_received。连接=test_connection
22
test_email_received。记录器=日志
23
24
test_email_received.run (trigger_params)
25
26
self.fail() #如果我们走到这里,运行循环不知何故失败了

从上到下:

@timeout_pass更改@timeout装饰器的超时行为。否则,测试将在超时时失败。使用函数包装器允许我们通过超时的单元测试。我们的触发器是无限循环,所以在这种情况下,我们没有遇到异常,这意味着循环是可能做我们期望它做的事。

@timeout_decorator.timeout(30)只是告诉测试在30秒后超时。您可以根据需要自由更改此延迟,这取决于您的触发器正在做什么。

@patch(“komand.Trigger。发送”, side_effect=fakeSender.send) - This tells the python unittetst framework to overwrite the behavior of komand.Trigger.send with the class we provide, fakeSender. In other words, we intercept anything the trigger tries to send to the orchestrator and print it out (using the fakeSender() class). This is a mock.

测试的其余部分与动作测试非常相似。我们使用来自适当示例的值设置触发器类和连接类。然后我们将触发器连接设置为测试连接。最后,我们运行触发器。

触发器的run()函数是一个无限循环,因此除了run不会返回异常之外,我们实际上无法测试任何具体的东西。因此,如果此测试通过run函数并点击self.fail()行,则触发器可能会发生意外情况。

触发单元测试:

         
蟒蛇
1
触发单元测试框架最基本的:
2
def试验_{{.ActionName}}_some_function_to_test(自我):
自我.失败(“未实施的测试”)

这里需要创建测试来测试触发器的逻辑。在某些情况下,这并非易事。一般的经验法则是,如果不能在触发器中测试代码行,则需要对其进行重构,因为在运行循环中有太多的逻辑。在简单触发器中,这可能没问题,但如果您要进行任何数据操作或转换,强烈建议将该行为移动到它们自己的方法或甚至一个utils文件中,以便进行单元测试。