繁体   English   中英

我需要模拟功能,但不起作用,如何在不修改src代码的情况下构建unittest?

[英]I need to mock function, but it doesn't work, How to build unittest without modify src code?

这是我要测试的功能:

@Component
public 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目录中工作的东西:

@Component
public class DataSourceAttributes {

    private GenericManager genericManager;  // First change

    public DataSourceAttributes () { // Second cahnge
        AbstractConnector abstractConnector = new AWSSecretManagerConnector("secretsmanager." + region + ".amazonaws.com", region);
        this.genericManager = new GenericManager(abstractConnector);
    }

    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());
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw e;
        }
        return awsSecretDB;
    }
}

当我运行测试时,System.out.println(位于getAttribsBySecret中)打印:

GENERIC MANAGER: genericManagerMock
awsSecretDB: Engine Test

如何显示,模拟效果很好。 所以,这是我的问题:如何在类中使用模拟并避免在主代码中声明新属性和构造函数。 我之所以这样问,是因为第一个代码有效,并且我不想对其进行编辑,我认为这不是单元测试的精神。

谢谢!

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

@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;
 }
}

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

如果您知道该代码有效,那么为什么首先要对其进行单元测试? 或者,如果您不打算更改源以修复该错误,为什么还要执行会引发错误的活动?

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

避免这种情况的发生是从微创更改开始,这些更改足以创建您需要的测试。 由于这是一种迁移方法,因此有时甚至可以使用肮脏的技巧来克服这一难题。 迈克尔·费瑟斯(Michael Feathers)在“有效地使用旧版代码”中提供了许多实用建议,对此进行了详细讨论。

在具有自省性的语言(如Java)中,有时自省可能是一种朝着更好的可测试解决方案发展的解决方案。 但是,这属于肮脏的把戏,除了作为一种迁移到更好的解决方案的方式之外,但是,我认为这不是可取的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM