eos-unittest 是白盒测试。

eos项目每次build的时候都会自动编译链接eos/unittest下的源文件,生成出eos/build/unittests/unit_test。后者是一个二进制源程序,运行会自动运行所有测试用例。
unit_test --list_content 枚举所有测试套及测试用例。
./unit_test -t eosio_system_tests/stake_unstake 单独测试eosio_system_tests测试套的stake_unstake测试用例。


eos-unittest采用的是boost库的单元测试套件。

BOOST_AUTO_TEST_SUITE(测试套名称) 声明测试套,放在定义最前面
BOOST_AUTO_TEST_SUITE_END() 测试套结束
BOOST_FIXTURE_TEST_CASE(测试用例名,测试类名) FIXTURE测试用例

class template_test_class : public TESTER
{// ...
};BOOST_AUTO_TEST_SUITE(template_test_suite)BOOST_FIXTURE_TEST_CASE(template_test_case1, template_test_class) try {// ...
} FC_LOG_AND_RETHROW()BOOST_AUTO_TEST_SUITE_END()

关于base_tester

base_tester类,测试类的基类。该类提供了测试框架的主要接口。
tester类继承于base_tester类,简单地拓展了base_tester(添加了3个跟区块相关的方法)。
我们自定义的测试类需要继承tester。

获取区块时间

control->head_block_time().sec_since_epoch()

校验用的宏

BOOST_REQUIRE_EQUAL 常用与两个简单对象(如asset)对比。
REQUIRE_MATCHING_OBJECT 常用与两个variant(尤其是mutable_variant_ojbect)对比。

生成区块

void base_tester::produce_blocks( uint32_t n = 1, bool empty = false );// exp
produce_blocks();
produce_blocks(3);

创建用户

vector<transaction_trace_ptr>  base_tester::create_accounts(vector<account_name> names,bool multisig = false,bool include_code = true
)// exp
create_accounts({N(hello), N(world)})

部署合约

// WAVM
void set_code( account_name name, const char* wast, const private_key_type* signer = nullptr
);// WABT
void set_code( account_name name, const vector<uint8_t> wasm, const private_key_type* signer = nullptr
);void set_abi( account_name name, const char* abi_json, const private_key_type* signer = nullptr
);// exp
#include <eosio.token/eosio.token.wast.hpp>
#include <eosio.token/eosio.token.abi.hpp>
// ...
set_code( N(eosio.token), eosio_token_wast );
set_abi( N(eosio.token), eosio_token_abi );

eos项目自带的eosio_build.sh会额外编译生成两个特殊hpp文件:

  • contract_name.abi.hpp
  • contract_name.wast.hpp
    里面是把abi和wast的数据转换成头文件,方便测试时应用。

设置序列化数据

在单元测试中,我们经常要对持久化数据表的数据进行反序列化,以方便我们对数据结构及相关成员的读取和校验。

// 初始化序列化器
// 注意下面的token_abi_ser.set_abi函数
const auto& accnt = control->db().get<account_object,by_name>( N(eosio.token) );
abi_def abi;
BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true);
token_abi_ser.set_abi(abi, abi_serializer_max_time);// 初始化完了以后,就可以用来序列化持久化的数据表
// 注意下面的token_abi_ser.binary_to_variant函数
{// ...vector<char> data = get_row_by_account( N(eosio.token), symbol_code, N(stat), symbol_code );return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time );
}

关于variant

有三种常用类型,用于弱化对象的类型,用一种通用的格式去标识所有智能合约中定义的数据结构。

  • variant
  • variant_object
  • mutable_variant_object

variant常用于数据的序列化与反序列化,表示一种通用的数据类型。
variant_object更像是一个对象的容器。
mutable_variant_object常用于定义复杂可嵌套的数据格式,可以通过as模板方法转换类型,[]访问成员。

提交TRX&ACTION

在eos-unittest中,有N种创建ACTION的方法:

class base_tester {public:/*两个push_transaction的接口一个接受packed_transaction,zlib压缩,已签名另一个接受signed_transaction,无压缩*/transaction_trace_ptr push_transaction(packed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(),uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US);transaction_trace_ptr push_transaction( signed_transaction& trx, fc::time_point deadline = fc::time_point::maximum(),uint32_t billed_cpu_time_us = DEFAULT_BILLED_CPU_TIME_US);/*四个push_action接口,会自动打包成一个transaction:1. 接收action对象和单一授权2. 接收各细分action的字段、单一授权3. 接收各细分action的字段、多active授权4. 接收各细分action的字段、多自定义授权*/action_result push_action(action&& cert_act, uint64_t authorizer);transaction_trace_ptr push_action(const account_name& code,const action_name& acttype,const account_name& actor,const variant_object& data,uint32_t expiration = DEFAULT_EXPIRATION_DELTA,uint32_t delay_sec = 0);transaction_trace_ptr push_action(const account_name& code,const action_name& acttype,const vector<account_name>& actors,const variant_object& data,uint32_t expiration = DEFAULT_EXPIRATION_DELTA,uint32_t delay_sec = 0);transaction_trace_ptr push_action(const account_name& code,const action_name& acttype,const vector<permission_level>& auths,const variant_object& data,uint32_t expiration = DEFAULT_EXPIRATION_DELTA,uint32_t delay_sec = 0);
};

其中,除了一个push_action是返回action_result,其他都是返回transaction_trace_ptr,具体定义如下。是不是很眼熟?transaction_trace_ptr跟我们通过HTTP接口获得的JSON数据结果字段是一致的。action_result是截取base_action_trace::console的字符串结果。

class base_tester
{typedef string action_result;static action_result success() { return string(); }static action_result error( const string& msg ) { return msg; }static action_result wasm_assert_msg( const string& msg ) { return "assertion failure with message: " + msg; }static action_result wasm_assert_code( uint64_t error_code ) { return "assertion failure with error code: " + std::to_string(error_code); }
};struct base_action_trace
{base_action_trace( const action_receipt& r ):receipt(r){}base_action_trace(){}action_receipt       receipt;action               act;bool                 context_free = false;fc::microseconds     elapsed;uint64_t             cpu_usage = 0;string               console;uint64_t             total_cpu_usage = 0; /// total of inline_traces[x].cpu_usage + cpu_usagetransaction_id_type  trx_id; ///< the transaction that generated this actionuint32_t             block_num = 0;block_timestamp_type block_time;fc::optional<block_id_type>     producer_block_id;flat_set<account_delta>         account_ram_deltas;
};struct action_trace : public base_action_trace {using base_action_trace::base_action_trace;vector<action_trace> inline_traces;
};struct transaction_trace;
using transaction_trace_ptr = std::shared_ptr<transaction_trace>;

下面是几个常用的例子

action_result hello(uint64_t param1,uint64_t param2,uint64_t param3
)
{auto data = fc::variant_object(fc::mutable_variant_object()("param1", param1)("param2", param2)("param3", param3));action act;act.account = N(code);act.name    = N(hello);act.data    = abi_serializer.variant_to_binary(abi_serializer.get_action_type(N(hello)), data, abi_serializer_max_time);return base_tester::push_action(std::move(act), uint64_t(signer));
}BOOST_REQUIRE_EQUAL(success(), hello(param1, param2, param3));
BOOST_REQUIRE_EQUAL(error("missing authority of alice"), hello(param1, param2, param3));transaction_trace_ptr hello(uint64_t param1,uint64_t param2,uint64_t param3
)
{// 使用第三种push_actionreturn base_tester::push_action(N(code), N(hello), vector<account_name>{ {N(actor_account1)}, {N(actor_account2)} },fc::mutable_variant_object()  ("param1", param1)("param2", param2)("param3", param3));// PS:mutable_variant_object的语法与常规的C++语法有点不同:// 1. 从mutable_variant_object定义开始,无须逗号“,”分隔// 2. 每个字段以小括号定义分割,先字段键值,后字段值,类似字典// 3. mutable_variant_object可以嵌套mutable_variant_object
}BOOST_REQUIRE_EQUAL(success(), hello(param1, param2, param3)->action_traces[0].console);

获取持久化数据

在写测试的过程中,判定前置条件和后置条件经常需要判断持久化数据,eos-unittest框架的base_tester测试类也提供了相关的接口。

vector<char> base_tester::get_row_by_account(uint64_t code, uint64_t scope,uint64_t table,const account_name& act
) const;

下面是例子

fc::variant get_tab1(uint64_t primarykey)
{vector<char> data = get_row_by_account(N(code), N(scope), N(tab1), primarykey);// 通过abi_serializer反序列化元数据为结构体格式数据,可以很方便的读取表中的字段。return data.empty() ? fc::variant() : abi_serializer::binary_to_variant("tab1_struct", data, abi_serializer_max_time);
}

在校验持久化数据的过程中,最经常用到的尤数获取账户余额,下面给出几种方法:

// 这是eosio.system_tester.cpp中的实现,实际上与get_row_by_account无太大差异
asset get_balance(const account_name& act) {//return get_currency_balance( config::system_account_name, symbol(CORE_SYMBOL), act );//temporary code. current get_currency_balancy uses table name N(accounts) from currency.h//generic_currency table name is N(account).const auto& db  = control->db();const auto* tbl = db.find<table_id_object, by_code_scope_table>(boost::make_tuple(N(eosio.token), act, N(accounts)));share_type result = 0;// the balance is implied to be 0 if either the table or row does not existif (tbl) {const auto *obj = db.find<key_value_object, by_scope_primary>(boost::make_tuple(tbl->id, symbol(CORE_SYMBOL).to_symbol_code()));if (obj) {// balance is the first field in the serializationfc::datastream<const char *> ds(obj->value.data(), obj->value.size());fc::raw::unpack(ds, result);}}return asset( result, symbol(CORE_SYMBOL) );
}// base_tester也提供了相关的接口
asset base_tester::get_currency_balance(const account_name& contract,const symbol&       asset_symbol,const account_name& account
) const;

eos-unittest相关推荐

  1. python selenium unittest_python+selenium+unittest——ui自动化的轻量级选择

    最近项目部分趋于稳定,部分功能进入了维护阶段,但每次的小改动都需要进行回归测试,回归的用例不多但也算是重复劳动浪费资源.为了节约这部分人力,我考虑引入web的ui自动化.之前在其他项目中用的都是jav ...

  2. linux下运行python unitest_Python unittest打印日志可以在Linux上运行,但在Windows上不行...

    我正在尝试编写一个unittest,它将stdout和stderr重定向到一个写在Windows网络驱动器上的文件.出于某些原因,相同的脚本(只有diff.是目录路径)可以在Linux上工作,但在Wi ...

  3. 【系列】EOS开发3 EOS提供的程序和工具

    上一篇文章使用了nodeos命令来启动eos服务,这一篇文章,就来介绍一下eos提供的相关程序和工具. nodeos EOSIO的核心守护进程,它可通过插件配置来启动一个节点. cleos 这是一个命 ...

  4. 虚拟货币市值回调到4100亿整数关口,EOS逆势站上100关口

    虚拟货币 虚拟货币市值 在过去24小时中,虚拟货币整体回调,市值为4100亿美元.只有EOS逆势上扬,已经站上了100元(17.5美元)上方. 虚拟货币市场 距离12月份最高点几乎只有一步之遥. EO ...

  5. 区块链 + 大数据:EOS存储

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 谈到区块链的存储,我们很容易联想到它的链式存储结构,然而区块链从比特币发展到今日当红的EOS,技术形态已经演化了10年之 ...

  6. EOS智能合约:system系统合约源码分析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. eosio.system 概览 笔者使用的IDE是VScode,首先来看eosio.system的源码结构.如下图所示. ...

  7. EOS Cleos 命令使用指南

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 命令参考 操作 语法 例子 获取所有命令 $ cleos 例子 获取所有子命令 $ cleos ${command} 例 ...

  8. 区块链3.0:拥抱EOS

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. EOS是当下最火的区块链技术,被社会广泛看好为下一代区块链3.0.不同于以太坊的学习,EOS的主语言是C++,本文作为E ...

  9. 了解EOS看这一篇就够了一、团队二、技术三、项目进度四、争议和风险五、展望

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 无论是混迹于币圈.链圈还是矿圈,对BTC(比特币).ETH(以太坊).EOS这三大主流币一定不会陌生,BTC让人们了解了 ...

最新文章

  1. ENTER键指定事件
  2. dos下打包整个java工程
  3. python快速上手下载_初学者如何尽快上手python
  4. html5包含inc文件,HTML中include file标签的用法
  5. redis入门——集群篇
  6. input层级高 小程序_微信小程序textarea层级过高(盖住其他元素)问题的解决办法...
  7. 修改文字处理布局及文字绘制,一个字的感受:太糙
  8. XMind8update6 补丁
  9. c#制作仿win7屏幕键盘之笔记
  10. 华为NCE网管配置EPLAN
  11. 抖音搬运新技术秒上热门,爆抖神器,效果惊人
  12. 1秒等于多少微妙,纳秒
  13. python创建通讯录_python实现简易通讯录修改版
  14. 详解 Flink Metrics 原理与监控实战
  15. 大数据面试常见问题(三)——Hadoop部分
  16. 华三防火墙web端口_华三防火墙开放端口 华三防火墙怎么登录
  17. Python+OpenCV判断图像是黑底还是白底
  18. github项目管理和贡献代码
  19. Emby识别都是英文海报
  20. 三年一跳槽、拒绝“唯学历”,火速 Get 这份程序员求生指南!

热门文章

  1. SAP BPC最佳实践-BPC系统备份及恢复
  2. fckeditor 中文乱码问题
  3. Corn Fields 玉米田
  4. bootstrap 兼容哪些浏览器
  5. 延时加载 lazyload使用技巧
  6. 手游项目初期的一些想法
  7. JavaScript 图片切割效果(带拖放、缩放效果)
  8. JAVA继承重写的规则
  9. 接口测试--apipost如何解决接口重定向
  10. 云联惠认证时间_云联惠最新消息2018 云联惠2018年最新消息