Best practice - Setting a field without setters in a unit test
getter-setter, java, mockito, reflection, unit-testing
Solution
you can set the value using reflection. It doesn't need any change in production code.
ReflectionTestUtils.setField(YourClass.class, "fieldName", fieldValue);
Problem
Let's say you have the following class you would like to test: ``` public class SomeService { public String someMethod(SomeEntity someEntity) { return someEntity.getSomeProperty(); } } ``` The SomeEntity looks like this: ``` public class SomeEntity { private String someProperty; public getSomeProperty() { return this.someProperty; } } ``` The assertion you would like to do can be the following: ``` String result = someService.someMethod(someEntity); assertThat(result).isEqualTo("someValue"); ``` How can you make this test work? 1) Add a setter for 'someProperty' in the SomeEntity class. I don't think this a good solution because you don't change production code to make your tests work. 2) Use ReflectionUtils to set the value of this field. Test would look like this: ``` public class TestClass { private SomeService someService; @Test public void testSomeProperty() { SomeEntity someEntity = new SomeEntity(); ReflectionTestUtils.setField(someEntity, "someProperty", "someValue"); String result = someService.someMethod(someEntity); assertThat(result).isEqualTo("someValue"); } } ``` 3) You create an inner class in your test class that extends the SomeEntity class and adds the setter for this field. However, for this to work you will also need to change the SomeEntity class because the field should become 'protected' instead of 'private'. Test class might look like this: ``` public class TestClass { private SomeService someService; @Test public void testSomeProperty() { SomeEntityWithSetters someEntity = new SomeEntityTestWithSetters(); someEntity.setSomeProperty("someValue"); String result = someService.someMethod(someEntity); assertThat(result).isEqualTo("someValue"); } public class SomeEntityWithSetters extends SomeEntity { public setSomeProperty(String someProperty) { this.someProperty = someProperty; } } } ``` 4) You use Mockito to mock SomeEntity. Seems fine if you only need to mock only one property in the class, but what if you need to mock like 10 properties are so. The test might look like this: ``` public class TestClass { private SomeService someService; @Test public void testSomeProperty() { SomeEntity someEntity = mock(SomeEntity.class); when(someEntity.getSomeProperty()).thenReturn("someValue"); String result = someService.someMethod(someEntity); assertThat(result).isEqualTo("someValue"); } } ```