I'm trying to unit test a method in a class that initializes some private fields:
public void init(Properties props) throws Exception {
this.language = props.getProperty(Constants.LANGUAGE,Constants.LANGUAGE_DEFAULT);
this.country = props.getProperty(Constants.COUNTRY,Constants.COUNTRY_DEFAULT);
try {
this.credits = Integer.valueOf(props.getProperty(Constants.CREDITS_OPTION_NAME, Constants.CREDITS_DEFAULT_VALUE));
} catch (NumberFormatException e) {
throw new Exception("Invalid configuration: 'credits' does not contain a valid integer value.", e);
}
//rest of method removed for sake of simplicity
}
My dilemma is tha开发者_开发技巧t I want to assert that the language, country and credits fields have been set after calling init, however they are private with no public accessor methods. I see there are two solutions for testing this:
- Make public methods for accessing the private fields, then call these methods after testing the init method.
- Make the unit test just call init method, and assume everything worked correctly is no exception was thrown.
What do you think is the ideal way to test this method?
I want to assert that the language, country and credits fields have been set after calling init
Some philosophy: why do you care whether they are set correctly? If the answer is "so the class then behaves correctly", then you can test that they have been set correctlty by testing that expected subsequent behaviour. If however setting them incorrectly has no effect they are redundant and you should remove them.
Another poster has suggested using reflection to access the private fields. I advise against this; anything marked private should be an implementation detail of the class, and therefore should not be directly tested. You should instead test the published API of your class. You then have the freedom to refactor it easilly by changing implementation (private) details.
You can use reflection to get the values of private fields. For example:
Field field = YourClass.class.getDeclaredField("language");
field.setAccessible(true); //override the access restriction of it being private
field.get(yourObject);
If there's spring on your classpath, you can use its ReflectionUtils
/ ReflectionTestUtils
.
It is sometimes argued that private fields should not be verified, and only the public state of the object is the point of unit-testing. From that perspective it may be better to expose a getter, or even better - thrown an exception.
If the object is in invalid state if the fields are not set, then it should be the responsibility of the object itself to enforce its validity, by throwing an exception.
I usually try to avoid tests that depend on the internal structure of the class under test. I'd much rather test this by testing some method that then uses these fields.
If you're doing strict TDD, you shouldn't even add those fields until you have a test case that actually takes advantage of them :)
Does your JUnit call the method directly? Then the reference to the Property Object is the same, you should be able to access Language and Country, for credits do the recalculation in your JUnit.
Public accessor for private methods is also a good choice.
Personally I would just check the public methods. Sometimes you need to check the internals, but this is rare.
BTW: An alternative to public getters is to make the unit test in the same package and provide package local getters.
精彩评论