16

When creating test cases for setters and getters of instance variables within the object. What is the best approach? Here I use the get and set methods within my tests. Would this be poor testing strategy?

/**
 * Test of setFlightNumber method, of class Flight.
 */
@Test
public void testSetFlightNumber() {
    System.out.println("setFlightNumber");
    int flightNumber = 1000;
    Flight instance = new Flight();
    instance.setFlightNumber(flightNumber);
    // TODO review the generated test code and remove the default call to fail.
    assertEquals(instance.getFlightNumber(), flightNumber);
}

/**
 * Test of getFlightNumber method, of class Flight.
 */
@Test
public void testGetFlightNumber() {
    System.out.println("getFlightNumber");
    Flight instance = new Flight();
    int expResult = 1000;
    instance.setFlightNumber(1000);
    int result = instance.getFlightNumber();
    assertEquals(expResult, result);
}
3
  • 4
    Why are you bothering to test setters and getters? That'd be my first question; unless your setters/getters are doing more than what they say they are (which is bad anyway), then you should rely on the fact that the assignment to a field won't fail. Also, you're ironically testing that they work in the same method, so if you were going to test this, you'd only need one method (since, without reflection, you wouldn't have a way to get back the value that was set without getting it). Commented Jan 25, 2014 at 18:15
  • 1
    Its a requirement for a lab. I have just been reading and many suggest that it is pointless. But lets just say one was required to test it just for the sake of testing. Would it be okay to use the getFlightNumber method within the setFlightNumber? Commented Jan 25, 2014 at 19:28
  • @Makoto, you just gave the best reason to test getters/setters: Because you want to make sure your setters/getters are not doing more than they say they are, precisely because that would be bad. I have found plenty of bugs in getters/setters in the wild. Think copy & paste, simply forgetting to modify the field name. Commented Jan 14, 2024 at 10:41

5 Answers 5

31

The main principle of unit testing is that you test a simple unit of code; that is, each method should be tested on its own merits.

This means we can't use the get method in our set test, and vice versa - you're not testing the individual unit of code that is the single method.

Given that...

Let's say we have a PlainOldJavaObject with a field value that we want (for some reason) to test the validity of setters and getters for. The only appropriate way to do this is through the use of reflection.

Here's my class declaration, which is pretty skinny:

public class PlainOldJavaObject {

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

I now set up my test class to make use of reflection; specifically using the Field class:

public class PlainOldJavaObjectTest {

    @Test
    public void testSetter_setsProperly() throws NoSuchFieldException, IllegalAccessException {
        //given
        final PlainOldJavaObject pojo = new PlainOldJavaObject();

        //when
        pojo.setValue("foo");

        //then
        final Field field = pojo.getClass().getDeclaredField("value");
        field.setAccessible(true);
        assertEquals("Fields didn't match", field.get(pojo), "foo");
    }

    @Test
    public void testGetter_getsValue() throws NoSuchFieldException, IllegalAccessException {
        //given
        final PlainOldJavaObject pojo = new PlainOldJavaObject();
        final Field field = pojo.getClass().getDeclaredField("value");
        field.setAccessible(true);
        field.set(pojo, "magic_values");

        //when
        final String result = pojo.getValue();

        //then
        assertEquals("field wasn't retrieved properly", result, "magic_values");
    }

}

In the first test, I am making certain that the field is read by reflectively accessing the value contained in the field for the instance of PlainOldJavaObject. Without violating the integrity of the declared class*, I am confident that the field is being set appropriately.

In the second test, I assume that the value has already been set to something prior, so the set-up involves populating the field with a known default value. When I read the value back, I am asserting that the value read back is the value that we know it was originally set to.

Ultimately, if you have a lot of setters and getters, you'll have to do code like this (since, if you rely on the assumption that your setters and getters "just work", like you are in your tests above, your test cases may be invalid).

*: Mind you, reflection is a quick and fast way to get into extreme trouble with undefined behavior, and you have few guarantees of object immutability. You're taking the shrink wrap off of the language and are doing strange and unusual things. Proceed at your own peril.

Sign up to request clarification or add additional context in comments.

7 Comments

Personally I feel the use of getters for testing a setter is more appropriate than using refection. I feel that reflection can lead to brittle tests and the coder should be able to refactor code without updating the test if the API does not change. I believe unit tests should test a portion of the API contract not the underlying code. The contract of a setter is that when called the getter returns the passed value. The underlying code could use an AtomicReference, Optional or any other holder and still meet the contract.
Why do you use the reflection for setting the parameters instead of just checking, that the value from getter is the same you have put into the constructor? Is it because the construct can't be trusted either?
@BBerry: It's all about isolating responsibilities. If I want to test that the value is retrieved via its get method without modification, I can't use any other method to put it there besides directly assigning the field.
@makoto but the contract of the getter and setter aren't that they modify a field in the class. The field isn't part of the public contact. The public contact is (presumably) that after calling the setter, the getter returns that value. That's what needs to be tested.
@makoto, sure there can be pathological cases (although if setFoo(x) is going to have a side effect on the value of x, it might not be appropriate to call it a setter, despite the name), but in the typical case, the only visible effect of setFoo(x) should be that the value returned by getFoo(x) is now different. And that contact can be implemented without having a field corresponding to foo in the class, so reflection isn't necessarily a reliable way to check this either. The field isn't part of the contact, the relationship between the methods is.
|
3

I think the approach used to test the getters and setter is perfectly valid and IMHO, I think that's how getter-setters should be tested.

A common statement I hear is that unit test should test only a unit, therefore, a single method of a class. However, in that case we are better off calling it single-method-testing. A unit, if possible, should be a method, but more than that it is a use-case!

Consider a class like this

public class PlainObject {

    void setA(int a) {
    }
 }

In this if you write any unit test that calls the method, it will pass because this method does nothing. And that is perfectly valid because calling this method is the only possible use case. It doesn't affect the behaviour of the application is any way.

It is correct to make one test that use both getters and setters because by itself you can't test the behaviour of the setter method. Moreover, the behaviour of setter doesn't even matter until you perform a get. If all I do it is call a setter and never get that value or use in any other way, there is no point of having a setter.

Using reflections, or other fancy ways only tests the implementation. These methods don't test the behaviour of your code. In the end you end up writing your code as test which is useless.

The only change I would make is change the name of the test method from testSetter to testAccess because you reading and updating the value.

Comments

1

Use Bean Runner API

This will automatically test all the getters and setters making sure that the values are properly set.

testBean

public void testBean(java.lang.Object bean)
              throws java.lang.Exception
Test the properties that have both getters and setters. Exclude those who have been excluded by excludeProperty(String). If the object implements Serializable, do a check to ensure it really is.
Parameters:
bean - the object to test
Throws:
java.lang.Exception - on failure

1 Comment

Will this cover Getters and Setters in Code Coverage ?
0

I believe that getter / setter testing does have value in that it could catch typo errors. However very little time should be spent on them since these errors are not frequent due to code generation tools. Hence using a tool to execute these test rather than writing them yourself is good practice.

There exist a library to help: OpenPojo

Couple other notes...

  • There is no need to use two test methods. Use one since it will exersize both the getter and setter.
  • Consider using a Theory to test a range of valid values (0, 1, Integer.MAX_VALUE). You might also use TestedOn to pass those int in-line example here
  • Test error conditions like passing -1

2 Comments

The github link is broken
@Hamed it was removed because I found libraries to do this. Updated post.
-1

the problem is that you have no way of knowing in either test whether the issue is in your getter or is in your setter, since you have to use one to test the other. you can either

  • use reflection

  • mark your private field as protected and have your test inherit from the target,

  • or don't test getters and setters

I'm still not experienced enough to tell what the best one is, but I know they all have drawbacks.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.