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

在下面的示例中,我想测试给定的源,是否构建了正确的 JSON 并将其映射到返回的对象中。起初,代码在其中创建了新的对象,如下所示:


@Override

public 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。我必须在对象内添加“名称”属性,或者我必须使其长度适合循环。

为了防止这种情况,我想介绍工厂内的新对象创建。现在:


@Override

public 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 方的实现细节?


慕丝7291255
浏览 105回答 1
1回答

宝慕林4294392

它总是一个妥协。但这种折衷是双向的:如果你对所有东西都使用工厂,那么是的,你将能够模拟出几乎所有东西,并且你不会在测试方法中有任何单一的“新”,但是,你的测试将看起来像一长串模拟,并且很难阅读/理解/维护测试及其 IMO 测试的强制性要求。还有一点需要考虑,黑盒测试比白盒测试要好得多。在您的情况下,您不返回JSONArray users,只需将其创建为内部变量即可进行内部计算。现在,理想情况下,测试应该检查给定输入参数列表,该方法是否返回预期值,仅此而已,测试不应该摆弄诸如“如果我想让它通过,我必须在这里创建内部值”之类的问题以这种特殊的方式,然后创造另一个类似的价值”。这一切都使得测试不明确且非常脆弱。所以这里有一些“经验法则”:总是喜欢黑盒测试。不要检查方法内部做了什么,最好在编写测试时甚至不要查看被测类的实现。始终尝试编写在给定参数集的情况下实际返回某些内容的方法 这将使测试更易于阅读和理解仅模拟/存根交互 - 该类需要的真正依赖项。通常这些并不多,而且它们出现在非常具体的点上。不要模拟内部变量的创建、就地完成的静态计算的结果或返回值。例子:// mocking example:class SomeService {&nbsp; &nbsp; private SomeDAO dao; // this is a real dependency, mock it in test}// don't mockMath.max(a,b)// don't mockLocalDateTime.of(...)// don't mockpublic int f() {&nbsp; ...&nbsp; List<Integer> internalVariable = new ArrayList<>(..)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java