In the following example, I want to test for the given source, if proper JSON is constructed and mapped into the returned object. At first, code had new Object creation inside it like this:
@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;
}
But it has problems, first of all, I don't want to use PowerMock or other reflection utils to mock new object creation. But to test this code;
- I had to return proper JSON inside response.getEntityContentAsString( ) because I didn't mock JSONArray, it should create proper object. This means I had to modify this 'dummy' json every time I want to test only this code behavior. I had to add the 'name' attribute inside the object or I had to make it proper length for the loop.
To prevent this I want to cover new object creation inside a factory. Now:
@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;
}
In my test, I can mock it instead of dealing custom JSON that works properly with the JSONArray class. Also, I don't have to deal with the implementation of the JSONArray since that library detail doesn't interest me in my function. But now it seems over-engineering because there are lots of cases like; JSONArray, JSONObject, JSONString, etc. in the project where they are created directly. Now team feels like they have to create all those factories JSONArrayFactory, JSONObjectFactory, etc.
What would you do in this example? May be we should change how we test the function? How do you deal with new object creations and prevent implementation details of 3rd parties?
Test of the given code:
@Test
public void getAttributes_givenResponse_shouldReturnAttributes( )
{
final Response response = mock( Response.class );
final JSONArray users = mock( JSONArray.class );
final JSONObject user = mock( JSONObject.class );
users.put( user );
final String sampleContentEntity = "";
final Integer sampleusersLength = 1;
final String simpleName = "name";
final Map<String, Object> expectedAttributes = new HashMap<>( );
final Set<String> mappedUsers = new HashSet<>( );
mappedUsers.add( simpleName );
expectedAttributes.put( "mappedUsers", mappedUsers );
when( response.getEntityContentAsString( ) ).thenReturn( sampleContentEntity );
when( jsonArrayFactory.create( eq( sampleContentEntity ) ) ).thenReturn( users );
when( users.length( ) ).thenReturn( sampleusersLength );
when( users.getJSONObject( anyInt( ) ) ).thenReturn( user );
when( user.getString( eq( "name" ) ) ).thenReturn( simpleName );
final Map<String, Object> attributes =
basicuserModule.getMappedAttributes( mock( Source.class ), response );
assertThat( attributes ).isEqualTo( expectedAttributes );
verify( response ).getEntityContentAsString( );
verify( jsonArrayFactory ).create( eq( sampleContentEntity ) );
verify( users ).getJSONObject( anyInt( ) );
}
Aucun commentaire:
Enregistrer un commentaire