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

如何在尝试实现可测试代码的同时防止过度工程?

如何在尝试实现可测试代码的同时防止过度工程?

慕丝7291255 2023-06-14 15:37:37
在下面的示例中,我想测试给定的源,是否构建了正确的 JSON 并将其映射到返回的对象中。起初,代码在其中创建了新的对象,如下所示:@Overridepublic Map<String, Object> getAttributes( Source source, Response response ){    Objects.requireNonNull( response, "response can not be null" );    final Map<String, Object> attributes = new HashMap<>( );    final JSONArray users = new JSONArray( response.getEntityContentAsString( ) );    final Set<String> mappedUsers = new HashSet<>( );    for ( int i = 0; i < users.length( ); i++ )    {        mappedUsers.add( users.getJSONObject( i ).getString( "name" ) );    }    attributes.put( "mappedUsers", mappedUsers );    return attributes;}但它有问题,首先,我不想使用 PowerMock 或其他反射实用程序来模拟新对象的创建。但是要测试这段代码;我必须在response.getEntityContentAsString( )中返回正确的 JSON ,因为我没有模拟 JSONArray,它应该创建正确的对象。这意味着每次我只想测试此代码行为时,我都必须修改此“虚拟”json。我必须在对象内添加“名称”属性,或者我必须使其长度适合循环。为了防止这种情况,我想介绍工厂内的新对象创建。现在:@Overridepublic Map<String, Object> getAttributes( Source source, Response response ){    Objects.requireNonNull( response, "response can not be null" );    final Map<String, Object> attributes = new HashMap<>( );    final JSONArray users = jsonArrayFactory.create( response.getEntityContentAsString( ) );    final Set<String> mappedUsers = new HashSet<>( );    for ( int i = 0; i < users.length( ); i++ )    {        mappedUsers.add( users.getJSONObject( i ).getString( "name" ) );    }    attributes.put( "mappedUsers", mappedUsers );    return attributes;}在我的测试中,我可以模拟它而不是处理与 JSONArray 类一起正常工作的自定义 JSON。此外,我不必处理 JSONArray 的实现,因为我对函数的库细节不感兴趣。但是现在它似乎过度设计了,因为有很多这样的情况;JSONArray、JSONObject、JSONString等直接在创建的项目中。现在团队觉得他们必须创建所有这些工厂 JSONArrayFactory、JSONObjectFactory 等。在这个例子中你会做什么?也许我们应该改变我们测试功能的方式?您如何处理新对象的创建并防止第 3 方的实现细节?
查看完整描述

1 回答

?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

它总是一个妥协。

但这种折衷是双向的:如果你对所有东西都使用工厂,那么是的,你将能够模拟出几乎所有东西,并且你不会在测试方法中有任何单一的“新”,但是,你的测试将看起来像一长串模拟,并且很难阅读/理解/维护测试及其 IMO 测试的强制性要求。

还有一点需要考虑,黑盒测试比白盒测试要好得多。在您的情况下,您不返回JSONArray users,只需将其创建为内部变量即可进行内部计算。

现在,理想情况下,测试应该检查给定输入参数列表,该方法是否返回预期值,仅此而已,测试不应该摆弄诸如“如果我想让它通过,我必须在这里创建内部值”之类的问题以这种特殊的方式,然后创造另一个类似的价值”。这一切都使得测试不明确且非常脆弱。

所以这里有一些“经验法则”:

  • 总是喜欢黑盒测试。不要检查方法内部做了什么,最好在编写测试时甚至不要查看被测类的实现。

  • 始终尝试编写在给定参数集的情况下实际返回某些内容的方法 这将使测试更易于阅读和理解

  • 仅模拟/存根交互 - 该类需要的真正依赖项。通常这些并不多,而且它们出现在非常具体的点上。不要模拟内部变量的创建、就地完成的静态计算的结果或返回值。

例子:

// mocking example:

class SomeService {

    private SomeDAO dao; // this is a real dependency, mock it in test

}


// don't mock

Math.max(a,b)


// don't mock

LocalDateTime.of(...)


// don't mock

public int f() {

  ...

  List<Integer> internalVariable = new ArrayList<>(..)

}


查看完整回答
反对 回复 2023-06-14
  • 1 回答
  • 0 关注
  • 104 浏览

添加回答

举报

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