为了账号安全,请及时绑定邮箱和手机立即绑定

我需要模拟函数,但它不起作用,如何在不修改 src 代码的情况下构建单元测试?

我需要模拟函数,但它不起作用,如何在不修改 src 代码的情况下构建单元测试?

阿晨1998 2021-11-11 13:30:15
这是我要测试的功能:@Componentpublic class DataSourceAttributes {    ...    ...    public AWSSecretDB getAttribsBySecret() throws Exception {        AbstractConnector abstractConnector = new AWSSecretManagerConnector("secretsmanager." + region + ".amazonaws.com", region);        GenericManager genericManager = new GenericManager(abstractConnector);        System.out.println("Generic Manager: " + genericManager);        AWSSecretDB awsSecretDB;        try {            awsSecretDB = genericManager.getSecretModel(secretName, AWSSecretDB.class);            System.out.println("awsSecretDB: " + awsSecretDB.getEngine()); // It must be mocked        } catch (Exception e) {            LOGGER.error(e.getMessage(), e);            throw e;        }        return awsSecretDB;    }}这是我当前的单元测试:public class DataSourceAttributesTest {    @InjectMocks    private DataSourceAttributes dataSourceAttributes;    @Mock    private GenericManager genericManagerMock;    @Test    public void AWSSecretDBGetAttribsBySecret() throws Exception {        AWSSecretDB awsSecretDB = new AWSSecretDB();        awsSecretDB.setEngine("Engine Test");        awsSecretDB.setDbname("DB Test");        awsSecretDB.setHost("Host Test");        when(genericManagerMock.getSecretModel(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(awsSecretDB);        dataSourceAttributes.getAttribsBySecret();        // The assert is missing, but it's not important for this question    }}我需要模拟genericManager来控制getSecretModel()函数,但它不起作用。当我运行我的测试时,System.out.println(位于 getAttribsBySecret 中)打印出以下消息,证明模拟不起作用:Generic Manager: co.com.bancolombia.commons.secretsmanager.manager.GenericManager@1349883我知道如果我使用以下代码,模拟效果很好,但我不想重新编码它已经在 src 目录中工作的东西:当我运行测试时,System.out.println(位于 getAttribsBySecret 中)打印:GENERIC MANAGER: genericManagerMockawsSecretDB: Engine Test它如何显示,模拟效果很好。所以,这是我的问题:如何在类中使用模拟并避免在主代码中声明新属性和构造函数。我问这个是因为第一个代码有效,我不想编辑它,我认为这不是单元测试的精髓。
查看完整描述

2 回答

?
动漫人物

TA贡献1815条经验 获得超10个赞

要使用单元测试,您首先需要使代码可测试。这可能需要一些代码更改。无法在方法内部模拟局部变量,因此您需要将这些变量作为参数传递给方法,或者在对象内部创建属性并将模拟对象传递给构造函数。


@Component

public class DataSourceAttributes {

  private AbstractConnector abstractConnector;

  private GenericManager genericManager;


  @Autowired // to ask Spring to inject dependencies

  public DataSourceAttributes(AbstractConnector abstractConnector, GenericManager genericManager) {

    this.abstractConnector = abstractConnector;

    this.genericManager = genericManager;

  }


  public AWSSecretDB getAttribsBySecret() throws Exception {

    System.out.println("Generic Manager: " + genericManager);


    AWSSecretDB awsSecretDB;

    try {

        awsSecretDB = genericManager.getSecretModel(secretName, AWSSecretDB.class);

        System.out.println("awsSecretDB: " + awsSecretDB.getEngine()); // It must be mocked

    } catch (Exception e) {

        LOGGER.error(e.getMessage(), e);

        throw e;

    }

    return awsSecretDB;

 }

}

然后在测试中创建对象的实例,将模拟依赖项传递给构造函数


查看完整回答
反对 回复 2021-11-11
?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

如果您知道代码有效,那么首先为什么要对其进行单元测试?或者,如果您不打算更改源来修复它,为什么要执行可能带来错误的活动?

但是,更严重的是,您似乎陷入了“遗留代码困境”:要修改代码,您宁愿进行适当的测试。要进行测试,您必须修改代码。

对此的逃避是从刚好足以创建您需要的那些测试的微创更改开始。由于这是一种迁移方法,有时甚至可以接受一些肮脏的技巧来克服困境。Michael Feathers 在“有效处理遗留代码”中详细讨论了这一点,并提供了许多实用建议。

在像 Java 这样具有自省功能的语言中,有时自省可以成为一个解决方案,以实现更好的可测试解决方案。然而,这属于肮脏的伎俩,除了作为迁移到更好解决方案的一种方式之外,我认为这是不可取的。


查看完整回答
反对 回复 2021-11-11
  • 2 回答
  • 0 关注
  • 175 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信