Skip to content

Commit b042b13

Browse files
committed
Add RegionSpyingBeanPostProcessor class to spy on Region beans managed in a Spring TestContext for testing purposes.
1 parent 6526499 commit b042b13

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
* or implied. See the License for the specific language governing
14+
* permissions and limitations under the License.
15+
*/
16+
package org.springframework.data.gemfire.tests.mock.beans.factory.config;
17+
18+
import static org.mockito.Mockito.spy;
19+
20+
import java.util.Arrays;
21+
import java.util.Set;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.StreamSupport;
24+
25+
import org.apache.geode.cache.Region;
26+
27+
import org.springframework.beans.BeansException;
28+
import org.springframework.beans.factory.config.BeanPostProcessor;
29+
import org.springframework.context.ApplicationContext;
30+
import org.springframework.data.gemfire.util.ArrayUtils;
31+
import org.springframework.data.gemfire.util.CollectionUtils;
32+
import org.springframework.lang.NonNull;
33+
import org.springframework.lang.Nullable;
34+
import org.springframework.util.StringUtils;
35+
36+
/**
37+
* Spring {@link BeanPostProcessor} that creates spies for all managed {@link Region Regions} (beans)
38+
* in the Spring {@link ApplicationContext}.
39+
*
40+
* @author John Blum
41+
* @see org.apache.geode.cache.Region
42+
* @see org.mockito.Mockito#spy(Object)
43+
* @see org.springframework.beans.factory.config.BeanPostProcessor
44+
* @see org.springframework.context.ApplicationContext
45+
* @since 0.0.22
46+
*/
47+
@SuppressWarnings("unused")
48+
public class RegionSpyingBeanPostProcessor implements BeanPostProcessor {
49+
50+
private final Set<String> regionBeanNames;
51+
52+
public RegionSpyingBeanPostProcessor(String... regionBeanNames) {
53+
this(Arrays.asList(ArrayUtils.nullSafeArray(regionBeanNames, String.class)));
54+
}
55+
56+
public RegionSpyingBeanPostProcessor(@NonNull Iterable<String> regionBeanNames) {
57+
58+
this.regionBeanNames =
59+
StreamSupport.stream(CollectionUtils.nullSafeIterable(regionBeanNames).spliterator(), false)
60+
.filter(StringUtils::hasText)
61+
.collect(Collectors.toSet());
62+
}
63+
64+
protected boolean isRegion(@Nullable Object target) {
65+
return target instanceof Region;
66+
}
67+
68+
protected boolean isRegionBeanNameMatch(@NonNull String beanName) {
69+
70+
return this.regionBeanNames.isEmpty()
71+
|| (StringUtils.hasText(beanName) && this.regionBeanNames.contains(beanName));
72+
}
73+
74+
/**
75+
* @inheritDoc
76+
*/
77+
@Override
78+
public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
79+
return isRegion(bean) && isRegionBeanNameMatch(beanName) ? doSpy(bean) : bean;
80+
}
81+
82+
protected @Nullable <T> T doSpy(@Nullable T target) {
83+
return target != null ? spy(target) : target;
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Copyright 2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
* or implied. See the License for the specific language governing
14+
* permissions and limitations under the License.
15+
*/
16+
package org.springframework.data.gemfire.tests.mock.beans.factory.config;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.doAnswer;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.never;
24+
import static org.mockito.Mockito.spy;
25+
import static org.mockito.Mockito.times;
26+
import static org.mockito.Mockito.verify;
27+
import static org.mockito.Mockito.verifyNoInteractions;
28+
29+
import org.junit.Test;
30+
31+
import org.apache.geode.cache.Region;
32+
33+
import lombok.EqualsAndHashCode;
34+
import lombok.Getter;
35+
import lombok.NonNull;
36+
import lombok.RequiredArgsConstructor;
37+
import lombok.ToString;
38+
39+
/**
40+
* Unit Tests for {@link RegionSpyingBeanPostProcessor}.
41+
*
42+
* @author John Blum
43+
* @see org.junit.Test
44+
* @see org.mockito.Mockito
45+
* @see org.apache.geode.cache.Region
46+
* @see org.springframework.data.gemfire.tests.mock.beans.factory.config.RegionSpyingBeanPostProcessor
47+
* @since 0.0.22
48+
*/
49+
public class RegionSpyingBeanPostProcessorUnitTests {
50+
51+
@Test
52+
public void spiesOnAllRegions() {
53+
54+
Region<?, ?> mockRegionOne = mock(Region.class, "MockRegionOne");
55+
Region<?, ?> mockRegionTwo = mock(Region.class, "MockRegionTwo");
56+
57+
RegionSpyingBeanPostProcessor beanPostProcessor = spy(new RegionSpyingBeanPostProcessor());
58+
59+
doAnswer(invocation -> invocation.getArgument(0)).when(beanPostProcessor).doSpy(any());
60+
61+
assertThat(beanPostProcessor).isNotNull();
62+
63+
assertThat(beanPostProcessor.postProcessAfterInitialization(mockRegionOne, "MockRegionOne"))
64+
.isEqualTo(mockRegionOne);
65+
66+
assertThat(beanPostProcessor.postProcessAfterInitialization(mockRegionTwo, "MockRegionTwo"))
67+
.isEqualTo(mockRegionTwo);
68+
69+
verify(beanPostProcessor, times(1)).doSpy(eq(mockRegionOne));
70+
verify(beanPostProcessor, times(1)).doSpy(eq(mockRegionTwo));
71+
72+
verifyNoInteractions(mockRegionOne, mockRegionTwo);
73+
}
74+
75+
@Test
76+
public void spiesOnTargetedRegions() {
77+
78+
Region<?, ?> mockRegionOne = mock(Region.class, "MockRegionOne");
79+
Region<?, ?> mockRegionTwo = mock(Region.class, "MockRegionTwo");
80+
81+
RegionSpyingBeanPostProcessor beanPostProcessor =
82+
spy(new RegionSpyingBeanPostProcessor("MockRegionOne"));
83+
84+
doAnswer(invocation -> invocation.getArgument(0)).when(beanPostProcessor).doSpy(any());
85+
86+
assertThat(beanPostProcessor).isNotNull();
87+
88+
assertThat(beanPostProcessor.postProcessAfterInitialization(mockRegionOne, "MockRegionOne"))
89+
.isEqualTo(mockRegionOne);
90+
91+
assertThat(beanPostProcessor.postProcessAfterInitialization(mockRegionTwo, "MockRegionTwo"))
92+
.isEqualTo(mockRegionTwo);
93+
94+
verify(beanPostProcessor, times(1)).doSpy(eq(mockRegionOne));
95+
verify(beanPostProcessor, never()).doSpy(eq(mockRegionTwo));
96+
97+
verifyNoInteractions(mockRegionOne, mockRegionTwo);
98+
}
99+
100+
@Test
101+
public void willNotSpyOnNonRegion() {
102+
103+
Object bean = "TEST";
104+
105+
RegionSpyingBeanPostProcessor beanPostProcessor = spy(new RegionSpyingBeanPostProcessor());
106+
107+
assertThat(beanPostProcessor.postProcessAfterInitialization(bean, "TestBeanName")).isEqualTo(bean);
108+
109+
verify(beanPostProcessor, never()).doSpy(any());
110+
}
111+
112+
@Test
113+
public void doSpyIsNullSafe() {
114+
assertThat(new RegionSpyingBeanPostProcessor().<Object>doSpy(null)).isNull();
115+
}
116+
117+
@Test
118+
public void doSpySpiesOnNonNullObject() {
119+
120+
User jonDoe = User.as("Jon Doe").identifiedBy(1L);
121+
User jonDoeSpy = new RegionSpyingBeanPostProcessor().doSpy(jonDoe);
122+
123+
assertThat(jonDoeSpy).isNotNull();
124+
assertThat(jonDoeSpy).isInstanceOf(User.class);
125+
assertThat(jonDoeSpy).isNotSameAs(jonDoe);
126+
assertThat(jonDoeSpy.getName()).isEqualTo(jonDoe.getName());
127+
}
128+
129+
@Test
130+
public void isRegionReturnsTrue() {
131+
assertThat(new RegionSpyingBeanPostProcessor().isRegion(mock(Region.class))).isTrue();
132+
}
133+
134+
@Test
135+
public void isRegionReturnsFalse() {
136+
assertThat(new RegionSpyingBeanPostProcessor().isRegion("TEST")).isFalse();
137+
}
138+
139+
@Test
140+
public void isRegionBeanNameMatchReturnsTrue() {
141+
142+
assertThat(new RegionSpyingBeanPostProcessor().isRegionBeanNameMatch("TestRegionBeanName")).isTrue();
143+
144+
assertThat(new RegionSpyingBeanPostProcessor("TestRegionBeanName")
145+
.isRegionBeanNameMatch("TestRegionBeanName")).isTrue();
146+
}
147+
148+
@Test
149+
public void isRegionBeanNameMatchReturnsFalse() {
150+
151+
assertThat(new RegionSpyingBeanPostProcessor("TestRegionBeanName")
152+
.isRegionBeanNameMatch("MockRegionBeanName")).isFalse();
153+
}
154+
155+
@Getter
156+
@ToString(of = "name")
157+
@EqualsAndHashCode(of = "name")
158+
@RequiredArgsConstructor(staticName = "as")
159+
static class User {
160+
161+
private Long id;
162+
163+
@NonNull
164+
private final String name;
165+
166+
public User identifiedBy(Long id) {
167+
this.id = id;
168+
return this;
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)