Skip to content

Commit 0110f1e

Browse files
committed
Improve SessionMock.getService default behavior with cleaner implementation
- Replace complex exclusion list with cleaner argThat() approach using helper method - Add isConfiguredService() helper to centralize service configuration logic - Fix circular dependency issue in setup code - Add comprehensive test demonstrating improved default behavior - Update documentation to explain the new approach - Maintain backward compatibility while providing true default mock behavior This change makes SessionMock more maintainable and provides the expected default behavior that developers would naturally expect from a mock implementation. Any service not explicitly configured now throws NoSuchElementException by default, matching the behavior of the real Session implementation.
1 parent 7b038cb commit 0110f1e

File tree

2 files changed

+106
-16
lines changed

2 files changed

+106
-16
lines changed

impl/maven-testing/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionMock.java

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.concurrent.ConcurrentHashMap;
3434
import java.util.function.Supplier;
3535

36+
import java.util.Set;
3637
import org.apache.maven.api.Artifact;
3738
import org.apache.maven.api.LocalRepository;
3839
import org.apache.maven.api.ProducedArtifact;
@@ -70,7 +71,6 @@
7071

7172
import static org.mockito.ArgumentMatchers.any;
7273
import static org.mockito.ArgumentMatchers.anyString;
73-
import static org.mockito.ArgumentMatchers.argThat;
7474
import static org.mockito.ArgumentMatchers.same;
7575
import static org.mockito.Mockito.doAnswer;
7676
import static org.mockito.Mockito.doReturn;
@@ -126,7 +126,9 @@
126126
* <p><strong>Service Behavior:</strong> The {@link Session#getService(Class)} method
127127
* throws {@link NoSuchElementException} for unknown services by default, matching
128128
* the behavior of the real Session implementation. This ensures consistent behavior
129-
* between tests and production code.</p>
129+
* between tests and production code. The implementation uses Mockito's {@code argThat}
130+
* matcher to selectively apply the default behavior only to services that are not
131+
* explicitly configured, providing a clean and maintainable approach.</p>
130132
*
131133
* @see InternalSession
132134
* @see LocalRepository
@@ -154,7 +156,7 @@ public static InternalSession getMockSession(LocalRepository localRepository) {
154156
when(session.createRemoteRepository(anyString(), anyString())).thenAnswer(iom -> {
155157
String id = iom.getArgument(0, String.class);
156158
String url = iom.getArgument(1, String.class);
157-
return session.getService(RepositoryFactory.class).createRemote(id, url);
159+
return repositoryFactory.createRemote(id, url);
158160
});
159161
when(session.createRemoteRepository(any()))
160162
.thenAnswer(iom -> repositoryFactory.createRemote(iom.getArgument(0, Repository.class)));
@@ -461,29 +463,43 @@ public static InternalSession getMockSession(LocalRepository localRepository) {
461463

462464
// Set up default behavior for getService to throw NoSuchElementException for unknown services
463465
// This matches the behavior of the real Session implementation
464-
// We use doAnswer to set up a fallback that only applies when no specific stub is found
466+
// We use doAnswer with argThat to only apply to services not explicitly configured above
465467
doAnswer(invocation -> {
466468
Class<?> serviceClass = invocation.getArgument(0, Class.class);
467469
throw new NoSuchElementException(serviceClass.getName());
468470
})
469471
.when(session)
470-
.getService(argThat(serviceClass ->
472+
.getService(ArgumentMatchers.argThat(serviceClass ->
471473
// Only throw for service classes that are NOT explicitly configured above
472-
!serviceClass.equals(RepositoryFactory.class)
473-
&& !serviceClass.equals(VersionParser.class)
474-
&& !serviceClass.equals(LocalRepositoryManager.class)
475-
&& !serviceClass.equals(ArtifactInstaller.class)
476-
&& !serviceClass.equals(ArtifactDeployer.class)
477-
&& !serviceClass.equals(ArtifactManager.class)
478-
&& !serviceClass.equals(ProjectManager.class)
479-
&& !serviceClass.equals(ArtifactFactory.class)
480-
&& !serviceClass.equals(ProjectBuilder.class)
481-
&& !serviceClass.equals(ModelXmlFactory.class)
482-
&& !serviceClass.equals(Lookup.class)));
474+
!isConfiguredService(serviceClass)));
483475

484476
return session;
485477
}
486478

479+
/**
480+
* List of service classes explicitly configured in the mock session.
481+
*/
482+
private static final Set<Class<?>> CONFIGURED_SERVICES = Set.of(
483+
RepositoryFactory.class,
484+
VersionParser.class,
485+
LocalRepositoryManager.class,
486+
ArtifactInstaller.class,
487+
ArtifactDeployer.class,
488+
ArtifactManager.class,
489+
ProjectManager.class,
490+
ArtifactFactory.class,
491+
ProjectBuilder.class,
492+
ModelXmlFactory.class,
493+
Lookup.class
494+
);
495+
496+
/**
497+
* Checks if a service class is explicitly configured in the mock session.
498+
*/
499+
private static boolean isConfiguredService(Class<?> serviceClass) {
500+
return CONFIGURED_SERVICES.contains(serviceClass);
501+
}
502+
487503
/**
488504
* Creates a mock session with enhanced getService behavior that throws NoSuchElementException
489505
* for unknown services.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.plugin.testing.stubs;
20+
21+
import java.io.File;
22+
import java.util.NoSuchElementException;
23+
24+
import org.apache.maven.api.Service;
25+
import org.apache.maven.api.services.ArtifactManager;
26+
import org.apache.maven.api.services.RepositoryFactory;
27+
import org.apache.maven.impl.InternalSession;
28+
import org.junit.jupiter.api.Test;
29+
30+
import static org.junit.jupiter.api.Assertions.assertNotNull;
31+
import static org.junit.jupiter.api.Assertions.assertThrows;
32+
33+
/**
34+
* Test to demonstrate the improved default behavior of SessionMock.getService().
35+
* This test shows that the mock correctly throws NoSuchElementException for unknown
36+
* services while still returning configured services.
37+
*/
38+
class SessionMockDefaultBehaviorTest {
39+
40+
private static final String LOCAL_REPO = System.getProperty("java.io.tmpdir") + File.separator + "test-repo";
41+
42+
/**
43+
* Test that demonstrates the clean default behavior:
44+
* - Configured services work correctly
45+
* - Unknown services throw NoSuchElementException by default
46+
* - No need to maintain explicit exclusion lists
47+
*/
48+
@Test
49+
void testCleanDefaultBehavior() {
50+
InternalSession session = SessionMock.getMockSession(LOCAL_REPO);
51+
52+
// Configured services should work
53+
assertNotNull(session.getService(RepositoryFactory.class));
54+
assertNotNull(session.getService(ArtifactManager.class));
55+
56+
// Unknown services should throw NoSuchElementException by default
57+
assertThrows(NoSuchElementException.class, () -> session.getService(CustomService.class));
58+
assertThrows(NoSuchElementException.class, () -> session.getService(AnotherCustomService.class));
59+
}
60+
61+
/**
62+
* Custom service interface for testing - should throw NoSuchElementException
63+
*/
64+
interface CustomService extends Service {
65+
void doSomething();
66+
}
67+
68+
/**
69+
* Another custom service interface for testing - should throw NoSuchElementException
70+
*/
71+
interface AnotherCustomService extends Service {
72+
void doSomethingElse();
73+
}
74+
}

0 commit comments

Comments
 (0)