Home > database >  Java - Constructor Unit testing
Java - Constructor Unit testing

Time:06-02

I have following Java configuration class which I need to unit test using JUnit:

public class Config {

private static final String AMQ_CONNECTION_URL_TEMPLATE = "failover:(%s)";
private final String awsAmqUrl;

public Config(String url, Optional<String> amqConnectionOptions, PropertiesManager propertiesManager) {
    String urlParameter = propertiesManager.getStringParameter(url);
    this.awsAmqUrl = constructAmqConnectionString(urlParameter, amqConnectionOptions);
}

private String constructAmqConnectionString(String urlParameter, Optional<String> connectionOptions) {
    if (connectionOptions.isPresent()) {
        urlParameter = Stream.of(urlParameter.split(","))
                .map(url -> url   "?"   connectionOptions.get())
                .collect(Collectors.joining(","));
    }
    return String.format(AMQ_CONNECTION_URL_TEMPLATE, urlParameter);
}

public ConnectionFactory getConnectionFactory() {
    ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(awsAmqUrl);
    return connectionFactory;
}
}

I am struggling to find an optimal solution for constructAmqConnectionString method unit testing as it's marked as private. There are 3 scenarios I am trying to cover:

  • urlParameter - comprises comma separated URLs (url1,url2), connectionOptions is not empty;
  • urlParameter - comprises comma separated URLs (url1,url2), connectionOptions is empty;
  • urlParameter - comprises single URL (url1), connectionOptions is not empty.

Current solution is to add a getter into Config class for awsAmqUrl field so that logic of constructor's call can be verified/tested:

public String getAwsAmqUrl() {
    return this.awsAmqUrl;
}

Tests itself have following logic:

@Test
public void verifyConstructorWithoutMqOptionsMultiBroker() {
    when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
    Optional<String> amqConnectionOptions = Optional.empty();
    config = new Config("url1,url2", amqConnectionOptions, propertiesManager);
    assertEquals(String.format("failover:(url1,url2)"),config.getAwsAmqUrl());
}

@Test
public void verifyConstructorWithMqOptionsMultiBroker() {
    when(propertiesManager.getStringParameter(any())).thenReturn("url1,url2");
    Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
    config = new Config("url1,url2", amqConnectionOptions, propertiesManager);        
    assertEquals(String.format("failover:(url1?%1$s,url2?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}

@Test
public void verifyConstructorWithMqOptionsSingleBroker() {
    when(propertiesManager.getStringParameter(any())).thenReturn("url1");
    Optional<String> amqConnectionOptions = Optional.of("optionTest=1");
    config = new Config("url1", amqConnectionOptions, propertiesManager);                
    assertEquals(String.format("failover:(url1?%1$s)",amqConnectionOptions.get()),config.getAwsAmqUrl());
}

Adding a getter just for Unit testing purposes doesn't feel the right thing to do as it's breaking encapsulation. Is there a better way to approach testing in such scenario?

CodePudding user response:

The only place that your class uses awsAmqUrl is in the getConnectionFactory method. So it looks like this is the method you'll have to use, to make sure the value of awsAmqUrl is correct. So instead of having a getter for awsAmqUrl, use something like

String storedUrl = objectUnderTest.getConnectionFactory().getBrokerUrl();

and then you can make assertions on that URL.

Sure, it makes your test dependent on the behaviour of ActiveMQConnectionFactory - but that's OK, since your class is tightly coupled to that particular class anyway.

  • Related