测试套件缩减方法
测试套件缩减方法是一个当前的研究课题。特别是,持续实践和回归测试要求最小化测试用例的数量。最近,许多智能合约已被部署在区块链实现中。由于其不可变性,测试在智能合约开发中扮演着更为重要的角色。STS 包实现了 k+1 测试套件缩减方法。该开发的软件生成并运行针对智能合约的最小测试用例集。该软件包的分层构造确保了能够测试任何遵循特定设计模式实现的智能合约。
抽象测试类的源代码
抽象类的操作独立于被测试的实际智能合约及其检查的交易。下面的清单 1 和 2 显示了生成测试用例并执行它们的 STS 包类的源代码。
AbstractTestSC 类的源代码如(清单 1)所示。
清单 1: AbstractTestSC 类的源代码。
java
package pl.gdansk.ug.smarTS;import java.util.ArrayList;
public abstract class AbstractTestSC {protected ArrayList<AbstractTransaction> transactions = new ArrayList<>();public AbstractTestSC(){initiateTransactions();}protected abstract void initiateTransactions();public final void runTestSuite(AbstractSmartContract sC){AbstractRunTestCases.runTestSuite(sC, transactions);}
}AbstractTestSC 类应由具体测试类扩展。因此,方法 initiateTransactions() 被定义为一个实例方法,以强制其在子类中被重写。
与 AbstractTestSC 类不同,AbstractRunTestCases 类是一个仅包含类方法的工具类。此类中的两个方法都标记有 static 关键字,并在类上下文中调用。执行这些方法所需的所有数据都以输入参数的形式传递给这些方法。所有方法调用都是在引用类型上进行的。该类可以为任何继承自 AbstractSmartContract 类的特定合约操作。
AbstractRunTestCases 类的源代码如(清单 2)所示。
清单 2: AbstractRunTestCases 类的源代码。
java
package pl.gdansk.ug.smarTS;import java.util.ArrayList;
abstract class AbstractRunTestCases {private static void runTest(AbstractSmartContract sC, AbstractTransaction tR, int trNumber, boolean expectedResult){boolean result = sC.checkSC(tR);boolean correct = result == expectedResult;System.out.println("Test no: " + (trNumber + 1) + ", test result: " + ((correct)?"PASS":"FAIL"));}static void runTestSuite(AbstractSmartContract sC, ArrayList<AbstractTransaction> transactions){System.out.println("Smart contract class: " + sC.getClass());for (int i = 0; i < transactions.size(); i++){runTest(sC, transactions.get(i), i, i == 0);}}
}runTestSuite() 方法为特定的智能合约运行测试用例。该方法使用在具体智能合约中声明的交易对象列表。它遍历整个测试交易列表,并为每个交易调用 runTest() 方法。runTest() 方法执行一个测试用例。该方法又调用特定智能合约中的 checkSC() 方法,该方法检查评估表达式返回的值。
这两个类都在 AbstractSmartContract 和 AbstractTransaction 抽象类上操作,这两个类分别表示智能合约和交易的通用形式。
STS 包来测试它们的可能性,无论它们代表的应用程序类型如何。此外,它们各自具有不同数量的验证规则。而且,SendEnergySC 智能合约中的验证规则仅使用实数作为数据类型。相比之下,InsuranceConclusionSC 智能合约主要操作于检查日期的验证规则。Lambda 表达式的使用允许您操作任何数据类型的变量。后一个智能合约的源代码以及准备好的测试类如下所示。
InsuranceConclusionSC 类的源代码如(清单 3)所示。使用 Lambda 表达式定义各个验证规则。
清单 3: InsuranceConclusionSC 类的源代码。
java
package pl.gdansk.ug.concreteSC;import pl.gdansk.ug.smarTS.AbstractSmartContract;
import java.time.LocalDate;
import java.util.Arrays;public class InsuranceConclusionSC extends AbstractSmartContract {public InsuranceConclusionSC(){rulesList = Arrays.asList(t -> ((InsuranceTransaction) t).getDateFrom().isBefore(LocalDate.now()),t -> ((InsuranceTransaction) t).getDateFrom().isBefore(((InsuranceTransaction) t).getDateTo()),t -> ((InsuranceTransaction) t).getInsuranceAmount() > 0,t -> ((InsuranceTransaction) t).getInsurancePremium() > 0,t -> ((InsuranceTransaction) t).getInsurancePremium() < ((InsuranceTransaction) t).getInsuranceAmount());}
}InsuranceConclusionSC 类从 AbstractSmartContract 类继承了 checkSC() 方法。此方法检查是否满足允许交易完成的条件。它有一个输入参数,即对要验证的交易对象的引用。该方法的实现可以被视为纯粹的,因为结果仅取决于输入数据。根据验证结果,该方法返回 true 或 false 值。来自面向对象编程的继承和来自函数式编程的 Lambda 表达式的结合,允许通过这一个方法检查各种类型的交易。该方法被实现为 final。因此,该方法对于子代智能合约的工作方式相同。
TestInsuranceConclusionSC 类重写了抽象类 AbstractTestSC 的 initiateTransactions() 方法。根据 k+1k+1 测试模式,对于 InsuranceConclusionSC 智能合约中的 5 个验证规则,在 TestInsuranceConclusionSC 类中创建了 6 个交易对象。这两个类都在 InsuranceTransaction 类的对象上操作。TestInsuranceConclusionSC 类的源代码如(清单 4)所示。
清单 4: TestInsuranceConclusionSC 类的源代码。
java
package pl.gdansk.ug.testSC;import pl.gdansk.ug.concreteSC.InsuranceTransaction;
import pl.gdansk.ug.smarTS.AbstractTestSC;
import java.time.LocalDate;public class TestInsuranceConclusionSC extends AbstractTestSC {public void initiateTransactions(){transactions.clear();transactions.add(new InsuranceTransaction(LocalDate.now(), LocalDate.now().plusYears(1), 250.0, 10000.0));transactions.add(new InsuranceTransaction(LocalDate.now().minusDays(1), LocalDate.now().plusYears(1), 250.0, 10000.0));transactions.add(new InsuranceTransaction(LocalDate.now(), LocalDate.now().minusMonths(1), 250.0, 10000.0));transactions.add(new InsuranceTransaction(LocalDate.now(), LocalDate.now().plusYears(1), 250.0, 0.0));transactions.add(new InsuranceTransaction(LocalDate.now(), LocalDate.now().plusYears(1), 0.0, 10000.0));transactions.add(new InsuranceTransaction(LocalDate.now(), LocalDate.now().plusYears(1), 250.0, 100.0));}
}这两个智能合约的测试套件可以通过运行 RunTestSuite 类来执行。在 RunTestSuite 类中声明了两个类变量(标记了可选的 static 修饰符)。List<AbstractSmartContract> 类型的 smartContractList 变量存储 AbstractSmartContract 类的子代类的对象,即特定的智能合约。此外,List<AbstractTestSC> 类型的 testClassesList 变量保存 AbstractTestSC 类的子类的对象,即特定的测试类。程序员需要通过实现两个方法:createSmartContracts() 和 createTestClasses(),将这两类对象添加到声明的变量中。为存储在 smartContractList 变量中的所有智能合约运行测试套件的功能在 main() 方法中实现。该方法运行 createSmartContracts() 和 createTestClasses() 方法。然后,在一个 for 循环中,它遍历 ArrayList 集合中的所有智能合约。
