使用模拟进行测试

如果使用正确的方法,模拟对象将非常有用。 我在需要驱动软件开发使用的帖子中分享了一些使用Mock Objects的经验。

使用模拟进行测试_第1张图片

在这篇文章中,我分享了两件事
–使用模拟进行基于合同的测试。
–用于组织模拟代码的模式。

基于合同的测试
让我们以正在构建汇款服务的场景为例。 这种服务类型的关键组件是货币转换器,银行服务和外汇服务。

50000英尺的虚拟外汇服务设计如下所示。

使用模拟进行测试_第2张图片

我们必须编写需要货币换算和银行转账服务的外汇服务。
这是基于接触的测试的理想方案。

FXService的代码段

public class FXService {
    private final CurrencyConverter currencyConverter;
    private final BankService bankService;
    private final double commissionPer;

    public String transfer(Money money, BankAccount destinationAccount, Currency target) {

        String sourceCurrency = money.currency().name();
        String targetCurrency = target.name();

        double commissionAmount = calculateCommission(money.amount());
        double fxRate = currencyConverter.convert(1, sourceCurrency, targetCurrency); // First interaction  

        double transferAmount = calculateTransferAmount(money, commissionAmount);
        double totalAmount = applyFxRate(transferAmount, fxRate);

        String transactionId = bankService.deposit(totalAmount, destinationAccount); // Second interaction 

        return transactionId;
    }
}

我们新的外汇服务必须遵循以下合同

  • 根据输入/输出合同与货币转换器和银行转账进行交互。
  • 对每个服务进行一次呼叫。

测试FX服务的一种方法是调用真实服务,但这意味着测试运行缓慢,并且在执行测试时必须依赖该服务。 有时,调用实时服务不是一种选择,因为它尚未开发。

聪明的方法是模拟这些合作者(货币转换器和银行转账)并使用模拟框架验证交互。

使用模拟进行测试的另一个优点是,它可以验证fxservice是否以预期的方式使用了货币和银行转帐服务。

让我们看一下基于模拟的测试。

@Test
    public void transfer_sgd_to_inr() {

        FXService fxService = new FXService(currencyConverter, bankService, 0.0d);
        BankAccount account = new BankAccount("1111-22222", "SuperStableBank");

        expect(currencyConverter.convert(1, "SGD", "INR")).andReturn(50d);
        expect(bankService.deposit(100d, account)).andReturn("99999");
        replay(currencyConverter, bankService);

        String id = fxService.transfer(new Money(SGD, 2d), account, INR);
        assertEquals("99999", id);

        verify(currencyConverter, bankService);
    }

该测试是使用EasyMock框架编写的,并且是模拟来自协作者的答复。

编写要阅读的测试

良好测试的重要属性之一是阅读愉快。

嘲弄会使目标更加难以实现,因为单元测试的安装代码将具有非常复杂的组装逻辑,这些逻辑将混合一些正常的对象集和某些模拟期望。 我敢肯定,您之前已经看过测试中的功能,该功能可用作类中所有测试所需的设置的转储场。

让我们看一下我们先前使用的一些模拟代码,并尝试对其进行改进

expect(currencyConverter.convert(1, "SGD", "INR")).andReturn(50d);
expect(bankService.deposit(100d, account)).andReturn("99999");
replay(currencyConverter, bankService);

另一种方式

@RegisterExtension
JUnit5Mockery context = new JUnit5Mockery();

context.checking(new Expectations() {{
            oneOf(currencyConverter).convert(1, "SGD", "INR");
            will(returnValue(50d));

            oneOf(bankService).deposit(100d, account);
            will(returnValue("99999"));
        }});

上面的两个代码都在做同样的事情,但是后来用jmock编写的代码具有很好的糖方法来表达同样的事情。
这有助于使期望保持清洁,并与正在测试的代码保持一致。 上下文中的协作对象被模拟掉了。 简单的模式,但在使测试可读性方面非常有效。

这篇文章中使用的代码可以在github上找到

翻译自: https://www.javacodegeeks.com/2020/04/testing-using-mocks.html

你可能感兴趣的:(使用模拟进行测试)