Best practice. Field setup without setters in unit test

Say you have the following class that you would like to test:

public class SomeService { public String someMethod(SomeEntity someEntity) { return someEntity.getSomeProperty(); } } 

SomeEntity is as follows:

 public class SomeEntity { private String someProperty; public getSomeProperty() { return this.someProperty; } } 

The statement you would like to make may be as follows:

 String result = someService.someMethod(someEntity); assertThat(result).isEqualTo("someValue"); 

How can you do this test work?

1) Add setter for 'someProperty' to the SomeEntity class. I do not think this is a good solution because you do not change the production code so that your tests work.

2) Use ReflectionUtils to set the value of this field. The test will 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 an installer for this field. However, for this you will also need to change the SomeEntity class, because the field must be “protected” instead of 'private'. A testing 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. It seems great if you only need to make fun of only one property in the class, but what if you need to mock 10 properties. The test may 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"); } } 
+5
source share
5 answers

You can set the value using reflection. It does not need any changes to the production code.

ReflectionTestUtils.setField (YourClass.class, "fieldName", fieldValue);

+5
source

You can add a setter with a default scope (with a closed package).

+2
source

Using junit testing SomeService.someMethod ()

alternative 1. you should not use this, since there is no need to modify the object for writing junit.

alternative 2 may be used.

alternative 3. again the same as 3, do not apply only to junit. how about when a class cannot be extended.

alternative 4. yes, a good option. mockito is used for the same reason.

0
source

What is the behavior / contract specific to SomeService that can be checked? Based on your skeleton code, there really isn’t. It will either throw NPE on a bad input, or return a string that may or may not be null, depending on the magic of Hibernate. Not sure if you really can check.

0
source

I have faced the same dilemma many times before, a quick solution is to make the field you want to cheat a secure package, or provide a secure setter. Of course, both will change the production code.

Alternatively, consider a dependency injection infrastructure such as Dagger . The following is an example:

 @Module class DripCoffeeModule { @Provides Heater provideHeater(Executor executor) { return new CpuHeater(executor); } } 

This JUnit test overrides the DripCoffeeModule binding for a Mockito mock object heater. The layout is introduced in CoffeeMaker, as well as in the test.

 public class CoffeeMakerTest { @Inject CoffeeMaker coffeeMaker; @Inject Heater heater; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module( includes = DripCoffeeModule.class, injects = CoffeeMakerTest.class, overrides = true ) static class TestModule { @Provides @Singleton Heater provideHeater() { return Mockito.mock(Heater.class); } } @Test public void testHeaterIsTurnedOnAndThenOff() { Mockito.when(heater.isHot()).thenReturn(true); coffeeMaker.brew(); Mockito.verify(heater, Mockito.times(1)).on(); Mockito.verify(heater, Mockito.times(1)).off(); } } 
0
source

Source: https://habr.com/ru/post/1214301/


All Articles