在Openstack社区提交代码后,reviewer一般都提出需要添加测试用例。可能有人会遇到只需修改几行code真正的功能实现就可以完成了,但是涉及到添加测试用例时却发现无从下手。这里以曾写过的一个patch为例,说明如何编写社区测试用例。
下图中第一个修改的文件是真正要更改的功能实现;第二个文件是为此功能增加的测试用例。可以看出test code部分的代码量比code本身增加的多得多。以下表述中,code表示要完成目标功能的代码,test code特指测试用例部分的代码。
图1
此patch处理的场景为当一个device使用的sg A的rules中配置remote为另一sg B,当sg B的member发生变化,conntrack中的相应entry要被删除。原始code中只处理了rule为ingress的情况,rule为egress未处理。(https://review.openstack.org/#/c/318679/)
1)确认测试用例的文件
要增加test code首先要明确test code要添加到哪个文件、哪个类中。一般更改code的文件路径与test code的文件路径有相关性。
neutron/agent/linux/ip_conntrack.py -->code path
neutron/tests/unit/agent/linux/test_iptables_firewall.py -->test code path
可以看到这里code文件为ip_conntrack.py,但test code文件却不是test_ip_conntrack.py。从此patch的应用场景可以看出,code代码是被iptables_firewall.py中的函数调用,所以test case会写在test_iptables_firewall.py文件中。code是由iptables_firewall.IptablesFirewallDriver的函数进行调用,所以test case要添加到IptablesFirewallTestCase中。
2)Code代码的流程
要想写好test code,关键要看code代码的执行流程。在此patch中,当执行函数_clean_deleted_remote_sg_members_conntrack_enteries时,才触发code的执行。
明确了code的执行流程,patch中对 code改变的部分,才能设计test code触发code执行,确定函数执行前需要定义变量。
图2
3)Test code中的变量和判断条件
2)中关键函数_clean_deleted_remote_sg_members_conntrack_enteries触发了patch中code的执行。我们设计测试用例时就要从此函数入手,考虑两个问题:1)此函数如何能够被触发执行? 2)此函数执行时需要已知哪些变量?
跟踪代码流程,图2中filter_defer_apply_off->_remove_conntrack_entries_from_sg_\updates->_clean_deleted_remote_sg_members_conntack_entries此调用过程都没有传递参数,且此过程必然执行(没有if)。当_clean_deleted_remote_sg_members_conntack_entries调用 delete_conntrack_state_by_remote_ips及之后的函数执行,也没有使用调用时传递参数之外的变量。
因此,设计测试用例时,需要执行filter_defer_apply_off触发关键函数的执行。而且执行filter_defer_apply_off之前需要对关键函数使用的变量提前定义好。
图3
由于关键函数执行前需要使用的变量都有了确定的定义,执行结果即是可预见的。这样就可以直接设计出判断执行结果正确与否的条件。比如,依据设计的test code的场景,预期当使用ipv4协议、sg rule为ingress会执行命令conntrack–D-f ipv4 –d 10.0.0.1 –w 10 –s 10.0.0.2。
执行_filter_defer_apply_off后,_clean_deleted_remote_sg_members_conntrack_enteries只是要被调用的函数之一,执行结果中utils_exec可能会执行其他我们并不关注的命令。这里使用判断条件assert_has_call,就可以只判断我们关注的calls。如果测试用例的结果是确切的,可以使用assertEqual、assertThat等。关于mock的使用,参考[1].
图4
4)考虑全面
最初版本的test code一般考虑都不全面,初步验证test code之后,可以增加对不同协议(tcp/udp/icmp)以及ipv4/ipv6的设计。注意代码的可复用性。
图5
参考链接:
[1] http://www.voidspace.org.uk/python/mock/mock.html