Merge "Support handling multiple static mocks."

This commit is contained in:
Christian Brunschen
2019-06-05 15:43:14 +00:00
committed by Android (Google) Code Review
8 changed files with 760 additions and 39 deletions

View File

@@ -62,7 +62,8 @@ public final class AppCompactorTest {
private CountDownLatch mCountDown;
@Rule
public TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig();
public TestableDeviceConfig.TestableDeviceConfigRule
mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
@Before
public void setUp() {

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.testables;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MultipleStaticMocksTest {
@Rule
public StaticMockFixtureRule mStaticMockFixtureRule =
new StaticMockFixtureRule(AB::new, CD::new);
private List<String> mCollected;
@Test
public void testMultipleStaticMocks() throws Exception {
mCollected = new ArrayList<>();
int n = 0;
A.a();
n = verifyCollected(n, "A.a");
D.b();
n = verifyCollected(n, "D.b");
C.b();
n = verifyCollected(n, "C.b");
B.a();
n = verifyCollected(n, "B.a");
}
private int verifyCollected(int n, String... last) {
assertThat(mCollected).hasSize(n + last.length);
assertThat(mCollected.subList(n, mCollected.size()))
.containsExactlyElementsIn(last).inOrder();
return n + last.length;
}
private static class A {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class B {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class C {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class D {
/* package */ static void a() {}
/* package */ static void b() {}
}
/**
* AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
*/
private class AB implements StaticMockFixture {
@Override
public StaticMockitoSessionBuilder setUpMockedClasses(
StaticMockitoSessionBuilder sessionBuilder) {
sessionBuilder.spyStatic(A.class);
sessionBuilder.spyStatic(B.class);
return sessionBuilder;
}
@Override
public void setUpMockBehaviors() {
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("A.a");
return null;
}).when(A::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("A.b");
return null;
}).when(A::b);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("B.a");
return null;
}).when(B::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("B.b");
return null;
}).when(B::b);
}
@Override
public void tearDown() {
}
}
/**
* AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
*/
private class CD implements StaticMockFixture {
@Override
public StaticMockitoSessionBuilder setUpMockedClasses(
StaticMockitoSessionBuilder sessionBuilder) {
sessionBuilder.spyStatic(C.class);
sessionBuilder.spyStatic(D.class);
return sessionBuilder;
}
@Override
public void setUpMockBehaviors() {
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("C.a");
return null;
}).when(C::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("C.b");
return null;
}).when(C::b);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("D.a");
return null;
}).when(D::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("D.b");
return null;
}).when(D::b);
}
@Override
public void tearDown() {
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.testables;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
/**
* Provides support for a set of static mocks for use within a single shared
* {@link StaticMockitoSession}.
*/
public interface StaticMockFixture {
/**
* Adds any required mock or spy classes managed by this {@link StaticMockFixture} to the
* {@link StaticMockitoSessionBuilder} provided.
*
* Call this to set up the classes that this expects to be mocked, by adding them to the
* {@link StaticMockitoSessionBuilder} using
* {@link StaticMockitoSessionBuilder#mockStatic(Class)},
* {@link StaticMockitoSessionBuilder#spyStatic(Class)} or similar as appropriate.
*
* @param sessionBuilder the {@link StaticMockitoSessionBuilder} to which the classes should be
* added to mock, spy, or otherwise as required
* @return sessionBuilder, to allow for fluent programming
*/
StaticMockitoSessionBuilder setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder);
/**
* Configures the behaviours of any mock or spy classes managed by this
* {@link StaticMockFixture}.
*
* Call this after {@link StaticMockitoSessionBuilder#startMocking()} has been called.
* This sets up any default behaviors for the mocks, spys, etc.
*/
void setUpMockBehaviors();
/**
* Tear everything down.
*/
void tearDown();
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.testables;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.quality.Strictness;
import java.util.function.Supplier;
/**
* <p>StaticMockFixtureRule is a {@link TestRule} that wraps one or more {@link StaticMockFixture}s
* to set them up and tear it down automatically. This works well when you have no other static
* mocks than the ones supported by their respective {@link StaticMockFixture}s.</p>
*
* <p>StaticMockFixtureRule should be defined as a rule on your test so it can clean up after
* itself. Like the following:</p>
* <pre class="prettyprint">
* public final StaticMockFixture mStaticMockFixtures = ...;
* &#064;Rule
* public final StaticMockFixtureRule mStaticMockFixtureRule =
* new StaticMockFixtureRule(mStaticMockFixtures);
* </pre>
*/
public class StaticMockFixtureRule implements TestRule {
private StaticMockitoSession mMockitoSession;
private StaticMockFixture[] mStaticMockFixtures;
private Supplier<? extends StaticMockFixture>[] mSupplier;
/**
* Constructs a StaticMockFixtureRule that always uses the same {@link StaticMockFixture}
* instance(s).
*
* @param staticMockFixtures the {@link StaticMockFixture}(s) to use.
*/
public StaticMockFixtureRule(StaticMockFixture... staticMockFixtures) {
mStaticMockFixtures = staticMockFixtures;
mSupplier = null;
}
/**
* Constructs a StaticMockFixtureRule that retrieves a new {@link StaticMockFixture} instance
* from one or more {@link Supplier<? extends StaticMockFixture >}s for each test invocation.
*
* @param supplier the {@link Supplier<? extends StaticMockFixture >}(s) that will supply the
* {@link StaticMockFixture}(s).
*/
@SafeVarargs
public StaticMockFixtureRule(Supplier<? extends StaticMockFixture>... supplier) {
mStaticMockFixtures = null;
mSupplier = supplier;
}
@Override
public Statement apply(Statement base, Description description) {
StaticMockitoSessionBuilder sessionBuilder = getSessionBuilder();
if (mSupplier != null) {
mStaticMockFixtures = new StaticMockFixture[mSupplier.length];
for (int i = 0; i < mSupplier.length; i++) {
mStaticMockFixtures[i] = mSupplier[i].get();
}
}
for (int i = 0; i < mStaticMockFixtures.length; i++) {
sessionBuilder = mStaticMockFixtures[i].setUpMockedClasses(sessionBuilder);
}
mMockitoSession = sessionBuilder.startMocking();
for (int i = 0; i < mStaticMockFixtures.length; i++) {
mStaticMockFixtures[i].setUpMockBehaviors();
}
return new TestWatcher() {
@Override
protected void succeeded(Description description) {
tearDown(null);
}
@Override
protected void failed(Throwable e, Description description) {
tearDown(e);
}
}.apply(base, description);
}
/**
* This allows overriding the creation of the builder for a new {@link StaticMockitoSession}.
* Mainly for testing, but also useful if you have other requirements for the session.
*
* @return a new {@link StaticMockitoSessionBuilder}.
*/
public StaticMockitoSessionBuilder getSessionBuilder() {
return mockitoSession().strictness(Strictness.LENIENT);
}
private void tearDown(Throwable e) {
mMockitoSession.finishMocking(e);
for (int i = mStaticMockFixtures.length - 1; i >= 0; i--) {
mStaticMockFixtures[i].tearDown();
if (mSupplier != null) {
mStaticMockFixtures[i] = null;
}
}
if (mSupplier != null) {
mStaticMockFixtures = null;
}
mMockitoSession = null;
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.testables;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.quality.Strictness.LENIENT;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
import java.util.function.Supplier;
/** Tests that StaticMockFixture manages fixtures and suppliers correctly. */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class StaticMockFixtureRuleTest {
private MockitoSession mMockitoSession;
@Mock private StaticMockitoSessionBuilder mSessionBuilder;
@Mock private StaticMockitoSession mSession;
@Mock private StaticMockFixture mA1;
@Mock private StaticMockFixture mB1;
@Mock private StaticMockFixture mA2;
@Mock private StaticMockFixture mB2;
@Mock private Supplier<StaticMockFixture> mSupplyA;
@Mock private Supplier<StaticMockFixture> mSupplyB;
@Mock private Statement mStatement;
@Mock private Description mDescription;
@Before
public void setUp() throws Throwable {
mMockitoSession = Mockito.mockitoSession()
.strictness(LENIENT)
.initMocks(this)
.startMocking();
prepareMockBehaviours();
}
@After
public void tearDown() {
mMockitoSession.finishMocking();
}
private void prepareFixtureMocks(StaticMockFixture... mocks) {
for (StaticMockFixture mock : mocks) {
when(mock.setUpMockedClasses(any())).thenAnswer(
invocation -> invocation.getArgument(0));
doNothing().when(mock).setUpMockBehaviors();
}
}
private void prepareMockBehaviours() throws Throwable {
when(mSessionBuilder.startMocking()).thenReturn(mSession);
when(mSupplyA.get()).thenReturn(mA1, mA2);
when(mSupplyB.get()).thenReturn(mB1, mB2);
prepareFixtureMocks(mA1, mA2, mB1, mB2);
when(mA1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
doNothing().when(mA1).setUpMockBehaviors();
when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
doNothing().when(mB1).setUpMockBehaviors();
doNothing().when(mStatement).evaluate();
doNothing().when(mA1).tearDown();
doNothing().when(mB1).tearDown();
}
private InOrder mocksInOrder() {
return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB,
mA1, mA2, mB1, mB2, mStatement, mDescription);
}
private void verifyNoMoreImportantMockInteractions() {
verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement);
}
@Test
public void testRuleWorksWithExplicitFixtures() throws Throwable {
InOrder inOrder = mocksInOrder();
StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
return mSessionBuilder;
}
};
Statement runMe = rule.apply(mStatement, mDescription);
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mA1).setUpMockBehaviors();
inOrder.verify(mB1).setUpMockBehaviors();
runMe.evaluate();
inOrder.verify(mStatement).evaluate();
// note: tearDown in reverse order
inOrder.verify(mB1).tearDown();
inOrder.verify(mA1).tearDown();
// Round two: use the same fixtures again.
rule.apply(mStatement, mDescription).evaluate();
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mA1).setUpMockBehaviors();
inOrder.verify(mB1).setUpMockBehaviors();
inOrder.verify(mStatement).evaluate();
// note: tearDown in reverse order
inOrder.verify(mB1).tearDown();
inOrder.verify(mA1).tearDown();
verifyNoMoreImportantMockInteractions();
}
@Test
public void testRuleWorksWithFixtureSuppliers() throws Throwable {
InOrder inOrder = mocksInOrder();
StaticMockFixtureRule rule = new StaticMockFixtureRule(mSupplyA, mSupplyB) {
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
return mSessionBuilder;
}
};
Statement runMe = rule.apply(mStatement, mDescription);
inOrder.verify(mSupplyA).get();
inOrder.verify(mSupplyB).get();
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mA1).setUpMockBehaviors();
inOrder.verify(mB1).setUpMockBehaviors();
runMe.evaluate();
inOrder.verify(mStatement).evaluate();
// note: tearDown in reverse order
inOrder.verify(mB1).tearDown();
inOrder.verify(mA1).tearDown();
// Round two: use the same suppliers again to retrieve different fixtures: mA2 and mB2
rule.apply(mStatement, mDescription).evaluate();
inOrder.verify(mSupplyA).get();
inOrder.verify(mSupplyB).get();
inOrder.verify(mA2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mB2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
inOrder.verify(mA2).setUpMockBehaviors();
inOrder.verify(mB2).setUpMockBehaviors();
inOrder.verify(mStatement).evaluate();
// note: tearDown in reverse order
inOrder.verify(mB2).tearDown();
inOrder.verify(mA2).tearDown();
verifyNoMoreImportantMockInteractions();
}
}

View File

@@ -17,7 +17,6 @@
package com.android.server.testables;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -31,14 +30,10 @@ import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.util.Pair;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.util.Collections;
@@ -48,20 +43,12 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
/**
* TestableDeviceConfig uses ExtendedMockito to replace the real implementation of DeviceConfig
* with essentially a local HashMap in the callers process. This allows for unit testing that do not
* modify the real DeviceConfig on the device at all.
*
* <p>TestableDeviceConfig should be defined as a rule on your test so it can clean up after itself.
* Like the following:</p>
* <pre class="prettyprint">
* &#064;Rule
* public final TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
* </pre>
* TestableDeviceConfig is a {@link StaticMockFixture} that uses ExtendedMockito to replace the real
* implementation of DeviceConfig with essentially a local HashMap in the callers process. This
* allows for unit testing that do not modify the real DeviceConfig on the device at all.
*/
public final class TestableDeviceConfig implements TestRule {
public final class TestableDeviceConfig implements StaticMockFixture {
private StaticMockitoSession mMockitoSession;
private Map<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>>
mOnPropertiesChangedListenerMap = new HashMap<>();
private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
@@ -73,14 +60,21 @@ public final class TestableDeviceConfig implements TestRule {
mKeyValueMap.clear();
}
/**
* {@inheritDoc}
*/
@Override
public Statement apply(Statement base, Description description) {
mMockitoSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
.startMocking();
public StaticMockitoSessionBuilder setUpMockedClasses(
StaticMockitoSessionBuilder sessionBuilder) {
sessionBuilder.spyStatic(DeviceConfig.class);
return sessionBuilder;
}
/**
* {@inheritDoc}
*/
@Override
public void setUpMockBehaviors() {
doAnswer((Answer<Void>) invocationOnMock -> {
String namespace = invocationOnMock.getArgument(0);
Executor executor = invocationOnMock.getArgument(1);
@@ -115,20 +109,15 @@ public final class TestableDeviceConfig implements TestRule {
String name = invocationOnMock.getArgument(1);
return mKeyValueMap.get(getKey(namespace, name));
}).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
}
return new TestWatcher() {
@Override
protected void succeeded(Description description) {
mMockitoSession.finishMocking();
mOnPropertiesChangedListenerMap.clear();
}
@Override
protected void failed(Throwable e, Description description) {
mMockitoSession.finishMocking(e);
mOnPropertiesChangedListenerMap.clear();
}
}.apply(base, description);
/**
* {@inheritDoc}
*/
@Override
public void tearDown() {
clearDeviceConfig();
mOnPropertiesChangedListenerMap.clear();
}
private static String getKey(String namespace, String name) {
@@ -210,4 +199,22 @@ public final class TestableDeviceConfig implements TestRule {
return properties;
}
/**
* <p>TestableDeviceConfigRule is a {@link TestRule} that wraps a {@link TestableDeviceConfig}
* to set it up and tear it down automatically. This works well when you have no other static
* mocks.</p>
*
* <p>TestableDeviceConfigRule should be defined as a rule on your test so it can clean up after
* itself. Like the following:</p>
* <pre class="prettyprint">
* &#064;Rule
* public final TestableDeviceConfigRule mTestableDeviceConfigRule =
* new TestableDeviceConfigRule();
* </pre>
*/
public static class TestableDeviceConfigRule extends StaticMockFixtureRule {
public TestableDeviceConfigRule() {
super(TestableDeviceConfig::new);
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.testables;
import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TestableDeviceConfigAndOtherStaticMocksTest {
@Rule
public StaticMockFixtureRule mStaticMockFixtureRule =
new StaticMockFixtureRule(TestableDeviceConfig::new, AB::new, CD::new);
private List<String> mCollected;
@Test
public void testDeviceConfigAndOtherStaticMocks() throws Exception {
mCollected = new ArrayList<>();
int n = 0;
String namespace = "foo";
String flag = "bar";
String flagValue = "new value";
Assert.assertNull(DeviceConfig.getProperty(namespace, flag));
A.a();
verifyCollected(++n, "A.a");
DeviceConfig.setProperty(namespace, flag, flagValue, false);
D.b();
verifyCollected(++n, "D.b");
Assert.assertEquals(flagValue, DeviceConfig.getProperty(namespace, flag));
C.b();
verifyCollected(++n, "C.b");
B.a();
verifyCollected(++n, "B.a");
}
private void verifyCollected(int n, String last) {
Assert.assertEquals(n, mCollected.size());
Assert.assertEquals(last, mCollected.get(n - 1));
}
private static class A {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class B {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class C {
/* package */ static void a() {}
/* package */ static void b() {}
}
private static class D {
/* package */ static void a() {}
/* package */ static void b() {}
}
/**
* AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
*/
private class AB implements StaticMockFixture {
@Override
public StaticMockitoSessionBuilder setUpMockedClasses(
StaticMockitoSessionBuilder sessionBuilder) {
sessionBuilder.spyStatic(A.class);
sessionBuilder.spyStatic(B.class);
return sessionBuilder;
}
@Override
public void setUpMockBehaviors() {
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("A.a");
return null;
}).when(A::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("A.b");
return null;
}).when(A::b);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("B.a");
return null;
}).when(B::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("B.b");
return null;
}).when(B::b);
}
@Override
public void tearDown() {
}
}
/**
* AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
*/
private class CD implements StaticMockFixture {
@Override
public StaticMockitoSessionBuilder setUpMockedClasses(
StaticMockitoSessionBuilder sessionBuilder) {
sessionBuilder.spyStatic(C.class);
sessionBuilder.spyStatic(D.class);
return sessionBuilder;
}
@Override
public void setUpMockBehaviors() {
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("C.a");
return null;
}).when(C::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("C.b");
return null;
}).when(C::b);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("D.a");
return null;
}).when(D::a);
ExtendedMockito.doAnswer(invocation -> {
mCollected.add("D.b");
return null;
}).when(D::b);
}
@Override
public void tearDown() {
}
}
}

View File

@@ -45,7 +45,8 @@ public class TestableDeviceConfigTest {
private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec
@Rule
public TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
public TestableDeviceConfig.TestableDeviceConfigRule
mTestableDeviceConfig = new TestableDeviceConfig.TestableDeviceConfigRule();
@Test
public void getProperty_empty() {