Статьи

Доступ к закрытым полям в модульных тестах

Прежде всего, позвольте мне сказать громче, вам нужно спроектировать свой код так, чтобы он был тестируемым, чтобы вы тестировали свои личные поля с помощью открытых методов.

Но («но» — это причины, по которым люди все еще программируют, а не сам компьютер, так что будьте счастливы здесь), иногда вы хотите и должны изменить некоторые частные поля, чтобы проверить все возможные границы.

Часто закрытые поля можно изменить с помощью общедоступных методов получения и установки или с помощью конструктора классов, и в этих случаях тесты легко создавать, и все довольны.

Но когда вы используете внешние фреймворки, такие как Spring, может оказаться, что вы не можете контролировать внедренные приватные поля.

Я уже объяснял, как макетировать компоненты пружин в ваших тестах без необходимости поддержки и создания специальных конфигураций тестовых пружин в предыдущем посте, здесь я покажу вам, как изменить частную переменную для ваших тестов.

Пусть говорят код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.google.common.collect.ImmutableSet;
@Service
public class SomeService {
 
        @Value("${whitelist.api.users:A,B,C}")
        private String apiUsers;
 
        private ImmutableSet<String> acceptableAPIBUsers;
 
        @PostConstruct
        public void init() {
                acceptableAPIBUsers = ImmutableSet.copyOf(apiUsers.replaceAll(" ", "").split(","));
        }
 
        public boolean isAnAcceptableUser(String user) {
                return user == null ? false : acceptableAPIBUsers.contains(user.toUpperCase());
        }
}

У нас нет контроля над строкой apiUsers, поэтому у нас есть несколько простых опций, одна из которых — создать конфигурацию Spring для вашего теста, изменить контекст Spring и смоделировать свойство, а вторая — создать установщик для изменения значения собственность из вашего теста.

Я не рекомендую создавать общедоступные оценщики только для ваших тестов, так как это может сбивать с толку других людей, которые смотрят на ваш код, и создание и поддержка конфигураций Spring для ваших тестов может быть трудной задачей.

Я знаю, что вы думаете: «Если я не смогу сделать что-либо из вышеперечисленного, меня уволят, моя девушка бросит меня, и моя жизнь закончится», но не беспокойтесь, я здесь, чтобы показать вам другой вариант!

продолжить

Вы можете создать класс с классическим методом для оценки вашего личного поля в тесте:

1
2
3
4
5
6
7
8
import groovy.transform.CompileStatic
@CompileStatic
class SomeServiceAccessor {
 
        public static void setApiUsers(SomeService someService,String apiUsers){
                someService.@apiUsers = apiUsers
        }
}

И используйте его в своем модульном тесте:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
public class SomeServiceTest {
 
        private SomeService service;
 
        @Before
        public void setUp() {
                service = new SomeSercvice();
                SomeSercviceAccessor.setApiUsers(service, "pippo,pluto,bungabunga");
                service.init();
        }
 
        @Test
        public void testIsNotApiUser() {
                assertThat(service.isAnRTBUser(""), is(false));
                assertThat(service.isAnRTBUser(null), is(false));
                assertThat(service.isAnRTBUser("random"), is(false));
        }
 
        @Test
        public void testIsRTBUser() {
                assertThat(service.isAnRTBUser("pippo"), is(true));
                assertThat(service.isAnRTBUser("PIPPO"), is(true));
                assertThat(service.isAnRTBUser("pluto"), is(true));
                assertThat(service.isAnRTBUser("bungabunga"), is(true));
        }
}

Конечно, вы можете сделать то же самое в java, изменив видимость поля с помощью отражения, но я думаю, что отличное решение может быть чище и проще.

Теперь я закончу этот пост следующей рекомендацией:

Не используйте это решение, если вам действительно не нужно изменять частные переменные для модульного тестирования вашего класса!