From 6bb58cd4d59c774762f5f2f10c58971ee7ad2e54 Mon Sep 17 00:00:00 2001 From: agustafson-atl <158539999+agustafson-atl@users.noreply.github.com> Date: Fri, 6 Dec 2024 16:07:18 +1100 Subject: [PATCH] Initial migration of code (#2) * Initial migration of code * Moved build.gradle.kts to correct location in root * build.gradle.kts: fixed wrapper reference --- README.md | 65 +++++++- build.gradle | 123 -------------- build.gradle.kts | 153 ++++++++++++++++++ settings.gradle => settings.gradle.kts | 0 .../java/net/jqwik/mockito/MockFinder.java | 68 ++++++++ .../jqwik/mockito/MockitoLifecycleHooks.java | 114 +++++++++++++ .../net/jqwik/mockito/AddingServiceTest.java | 78 +++++++++ .../net/jqwik/mockito/ProductServiceTest.java | 59 +++++++ .../jqwik/mockito/testcase/AddingService.java | 16 ++ .../mockito/testcase/CountingService.java | 8 + .../mockito/testcase/LoggingService.java | 8 + .../net/jqwik/mockito/testcase/Product.java | 10 ++ .../mockito/testcase/ProductRepository.java | 8 + .../mockito/testcase/ProductService.java | 20 +++ 14 files changed, 605 insertions(+), 125 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts rename settings.gradle => settings.gradle.kts (100%) create mode 100644 src/main/java/net/jqwik/mockito/MockFinder.java create mode 100644 src/main/java/net/jqwik/mockito/MockitoLifecycleHooks.java create mode 100644 src/test/java/net/jqwik/mockito/AddingServiceTest.java create mode 100644 src/test/java/net/jqwik/mockito/ProductServiceTest.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/AddingService.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/CountingService.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/LoggingService.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/Product.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/ProductRepository.java create mode 100644 src/test/java/net/jqwik/mockito/testcase/ProductService.java diff --git a/README.md b/README.md index 8f09acb..9f25454 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,72 @@ # Mockito Support for jqwik +Provides `net.jqwik.api.lifecycle.LifecycleHook`s to enable usage of Mockito with jqwik. + - Creates mocks for any fields with Mockito annotations such as `org.mockito.Mock` or `org.mockito.Spy`. + - Resets all mocks between each try, whether those mocks were created programmatically (eg: via calls to `Mockito.mock()`}) or via annotations. + ## How to use ### Maven and Gradle configuration +Maven: +```xml + + net.jqwik.mockito + jqwik-mockito + $LATEST_VERSION + test + +``` + +Gradle: +``` +testImplementation("net.jqwik.mockito:jqwik-mockito:$LATEST_VERSION") +``` ### Usage in Tests +```java +import net.jqwik.api.lifecycle.AddLifecycleHook; +import net.jqwik.api.*; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; -## Compatibility +import static org.mockito.Mockito.*; + +@AddLifecycleHook(MockitoLifecycleHooks.class) +class OrderServiceTest { + // Either @Mock or Mockito.mock() can be used. + @Mock + private OrderRepository orderRepository; + private OrderRepository orderRepository2 = Mockito.mock(); + + // Either @Spy or Mockito.spy() can be used. + @Spy + private ConsoleLoggingServiceImpl loggingService; + private LoggingService loggingService2 = Mockito.spy(new ConsoleLoggingServiceImpl()); -- Which Mockito versions are supported? \ No newline at end of file + @InjectMock + private OrderService orderService; + + @Group + class BasicExamples { + @Example + void testBasicExample() { + when(orderRepository.getOrder(any())).thenReturn(null); + // testing code + } + } + + @Group + class SomeProperties { + @Property + void testProperty() { + when(orderRepository.getOrder(any())).thenReturn("orderId"); + // testing code + } + } +} +``` + +## Compatibility +This is built against Mockito v4, and should support for all versions. diff --git a/build.gradle b/build.gradle deleted file mode 100644 index a33e90e..0000000 --- a/build.gradle +++ /dev/null @@ -1,123 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'signing' -} - -static isSnapshotRelease(versionString) { - versionString.endsWith('SNAPSHOT') -} - -ext { - moduleName = 'net.jqwik.mockito' - jqwikMockitoVersion = '1.0.0-SNAPSHOT' - jqwikVersion = '1.9.2' - mockitoVersion = '4.11.0' - isSnapshotRelease = isSnapshotRelease(jqwikMockitoVersion) -} - - -group = moduleName -version = jqwikMockitoVersion -description = "Jqwik Mockito support module" - -repositories { - mavenCentral() - maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots' } -} - -dependencies { - api("net.jqwik:jqwik:${jqwikVersion}") - - // To allow people to use their own version of Mockito - compileOnly("org.mockito:mockito-core:${mockitoVersion}") - - - testImplementation("org.mockito:mockito-core:${mockitoVersion}") -} - -jar { - archiveBaseName.set('jqwik-mockito') - archiveVersion.set("${jqwikMockitoVersion}") - manifest { - attributes('Automatic-Module-Name': "${moduleName}") - } -} - -java { - withJavadocJar() - withSourcesJar() - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -compileTestJava { - options.compilerArgs += '-parameters' - options.encoding = 'UTF-8' -} - -test { - useJUnitPlatform { - includeEngines 'jqwik' - } -} - -publishing { - repositories { - maven { - // hint: password is in ~/.gradle/gradle.properties - def ossrhUsername = project.hasProperty('ossrhUsername') ? project.ossrhUsername : '' - def ossrhPassword = project.hasProperty('ossrhPassword') ? project.ossrhPassword : '' - - credentials { - username = ossrhUsername - password = ossrhPassword - } - - // change URLs to point to your repos, e.g. http://my.org/repo - def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" - def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" - url = isSnapshotRelease ? snapshotsRepoUrl : releasesRepoUrl - } - } - publications { - jqwikMockito(MavenPublication) { - groupId = 'net.jqwik' - artifactId = 'jqwik-mockito' - from components.java - pom { - groupId = 'net.jqwik' - name = 'jqwik-mockito' - description = "Jqwik Mockito support module" - url = 'https://github.com/jqwik-team/jqwik-mockito' - licenses { - license { - name = 'Eclipse Public License - v 2.0' - url = 'http://www.eclipse.org/legal/epl-v20.html' - } - } - developers { - developer { - id = 'agustafson-atl' - name = "Andrew Gustafson" - email = "agustafson@atlassian.com" } - } - scm { - connection = 'scm:git:git://github.com/jqwik-team/jqwik-mockito.git' - developerConnection = 'scm:git:git://github.com/jqwik-team/jqwik-mockito.git' - url = 'https://github.com/jqwik-team/jqwik-mockito' - } - } - } - } -} - -signing { - if (!isSnapshotRelease) { - sign publishing.publications.jqwikMockito - } -} - -wrapper { - gradleVersion = "8.11.1" // upgrade with: ./gradlew wrapper -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..125604b --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,153 @@ +plugins { + id("java-library") + id("maven-publish") + id("signing") + id("com.diffplug.spotless") version "6.13.0" +} + +fun isSnapshotRelease(versionString: String): Boolean { + return versionString.endsWith("SNAPSHOT") +} + +val githubProjectName = "jqwik-team" +val artifactId = "jqwik-mockito" +val moduleGroupId = "net.jqwik" +val moduleName = "net.jqwik.mockito" +val junitPlatformVersion = "1.11.2" +val junitJupiterVersion = "5.11.2" +val opentest4jVersion = "1.3.0" +val assertJVersion = "3.26.3" +val mockitoVersion = "4.11.0" // Mockito 5+ no longer supports Java 8 +val jqwikVersion = "1.9.1" +val lombokVersion = "1.18.34" +val jqwikMockitoVersion = "0.0.1-SNAPSHOT" +val isSnapshotRelease = isSnapshotRelease(jqwikMockitoVersion) + +group = moduleName +version = jqwikMockitoVersion +description = "Jqwik Mockito support module" + +repositories { + mavenCentral() + maven(url = "https://s01.oss.sonatype.org/content/repositories/snapshots") +} + +tasks.jar { + archiveBaseName.set(artifactId) + archiveVersion.set(jqwikMockitoVersion) + manifest { + attributes("Automatic-Module-Name" to moduleName) + } +} + +spotless { + java { + palantirJavaFormat("1.1.0") + + importOrder("", "java|javax", "\\#") + removeUnusedImports() + } +} + +java { + withJavadocJar() + withSourcesJar() + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + +tasks.compileTestJava { + options.compilerArgs.add("-parameters") + options.encoding = "UTF-8" +} + +tasks.named("check") { + dependsOn(tasks.named("spotlessApply")) +} + +tasks.test { + useJUnitPlatform { + includeEngines("jqwik") + } +} + +dependencies { + api("org.opentest4j:opentest4j:${opentest4jVersion}") + api("net.jqwik:jqwik:${jqwikVersion}") + compileOnly("org.mockito:mockito-core:${mockitoVersion}") + compileOnly("org.mockito:mockito-junit-jupiter:${mockitoVersion}") + + testCompileOnly("org.projectlombok:lombok:${lombokVersion}") + testAnnotationProcessor("org.projectlombok:lombok:${lombokVersion}") + + testImplementation("org.junit.jupiter:junit-jupiter:${junitJupiterVersion}") + testImplementation("org.assertj:assertj-core:${assertJVersion}") + testImplementation("org.mockito:mockito-junit-jupiter:${mockitoVersion}") +} + +publishing { + repositories { + maven { + // hint: password is in ~/.gradle/gradle.properties + val ossrhUsername: String = project.findProperty("ossrhUsername") as? String ?: "" + val ossrhPassword: String = project.findProperty("ossrhPassword") as? String ?: "" + + credentials { + username = ossrhUsername + password = ossrhPassword + } + + // change URLs to point to your repos, e.g. http://my.org/repo + val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = uri(if (isSnapshotRelease) { snapshotsRepoUrl } else { releasesRepoUrl }) + } + } + publications { + create("jqwikMockito") { + groupId = moduleGroupId + artifactId = artifactId + from(components["java"]) + pom { + groupId = moduleGroupId + name = artifactId + description = "Jqwik Mockito support module" + url = "https://github.org/$githubProjectName/$artifactId" + licenses { + license { + name = "Eclipse Public License - v 2.0" + url = "http://www.eclipse.org/legal/epl-v20.html" + } + } + developers { + developer { + id = "agustafson-atl" + name = "Andrew Gustafson" + email = "agustafson@atlassian.com" + } + developer { + id = githubProjectName + name = "Johannes Link" + email = "business@johanneslink.net" + } + } + scm { + connection = "scm:git:git://github.com/$githubProjectName/$artifactId.git" + developerConnection = "scm:git:git://github.com/$githubProjectName/$artifactId.git" + url = "https://github.com/$githubProjectName/$artifactId" + } + } + } + } +} + +signing { + if (!isSnapshotRelease) { + sign(publishing.publications["jqwikMockito"]) + } +} + +tasks.wrapper { + gradleVersion = "8.11.1" // upgrade with: ./gradlew wrapper +} diff --git a/settings.gradle b/settings.gradle.kts similarity index 100% rename from settings.gradle rename to settings.gradle.kts diff --git a/src/main/java/net/jqwik/mockito/MockFinder.java b/src/main/java/net/jqwik/mockito/MockFinder.java new file mode 100644 index 0000000..1a6124c --- /dev/null +++ b/src/main/java/net/jqwik/mockito/MockFinder.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito; + +import org.mockito.Mock; +import org.mockito.MockingDetails; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.plugins.MemberAccessor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Helper class which extracts mocked fields from a given test instance. + */ +class MockFinder { + /** + * The set of annotations which indicate that a field is a Mockito mock. + */ + private static final Set> MOCKING_ANNOTATION_TYPES; + + static { + MOCKING_ANNOTATION_TYPES = new HashSet<>(); + MOCKING_ANNOTATION_TYPES.add(Mock.class); + MOCKING_ANNOTATION_TYPES.add(Spy.class); + } + + private static final MemberAccessor MEMBER_ACCESSOR = Plugins.getMemberAccessor(); + + /** + * Retrieve mocking fields for a given test instance object. + * + * @param testInstance The test instance which contains the mocked fields + * @return The list of mock objects which have been injected into the test instance + * @throws IllegalAccessException If it is not possible to get access to the fields in the test instance + */ + static List getMocks(Object testInstance) throws IllegalAccessException { + final List mocks = new ArrayList<>(); + for (final Field field : testInstance.getClass().getDeclaredFields()) { + if (isFieldMockitoAnnotated(field)) { + mocks.add(MEMBER_ACCESSOR.get(field, testInstance)); + } else { + final Object fieldValue = MEMBER_ACCESSOR.get(field, testInstance); + final MockingDetails mockingDetails = Mockito.mockingDetails(fieldValue); + if (mockingDetails.isMock()) { + mocks.add(fieldValue); + } + } + } + return mocks; + } + + private static boolean isFieldMockitoAnnotated(Field field) { + for (Annotation annotation : field.getAnnotations()) { + if (MOCKING_ANNOTATION_TYPES.contains(annotation.annotationType())) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/net/jqwik/mockito/MockitoLifecycleHooks.java b/src/main/java/net/jqwik/mockito/MockitoLifecycleHooks.java new file mode 100644 index 0000000..f623449 --- /dev/null +++ b/src/main/java/net/jqwik/mockito/MockitoLifecycleHooks.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito; + +import net.jqwik.api.lifecycle.AroundPropertyHook; +import net.jqwik.api.lifecycle.AroundTryHook; +import net.jqwik.api.lifecycle.Lifespan; +import net.jqwik.api.lifecycle.PropagationMode; +import net.jqwik.api.lifecycle.PropertyExecutionResult; +import net.jqwik.api.lifecycle.PropertyExecutor; +import net.jqwik.api.lifecycle.PropertyLifecycleContext; +import net.jqwik.api.lifecycle.Store; +import net.jqwik.api.lifecycle.TryExecutionResult; +import net.jqwik.api.lifecycle.TryExecutor; +import net.jqwik.api.lifecycle.TryLifecycleContext; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.internal.configuration.plugins.Plugins; +import org.mockito.internal.session.MockitoSessionLoggerAdapter; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Provides {@link net.jqwik.api.lifecycle.LifecycleHook}s to enable usage of Mockito with jqwik. + * + * Creates mocks for any fields with Mockito annotations such as {@link org.mockito.Mock} or {@link org.mockito.Spy}. + * Resets all mocks between each try, whether those mocks were created programmatically (eg: via calls to {@code Mockito.mock()}) + * or via annotations. + * + * + * + * import net.jqwik.api.lifecycle.AddLifecycleHook; + * import org.mockito.InjectMocks; + * import org.mockito.Mock; + * import org.mockito.Mockito; + * import org.mockito.Spy; + * {@literal @}AddLifecycleHook(MockitoLifecycleHooks.class) + * class OrderServiceTest { + * {@literal @}Mock + * private OrderRepository orderRepository; + * private OrderRepository orderRepository2 = Mockito.mock(); + * private LoggingService loggingService = Mockito.spy(new ConsoleLoggingServiceImpl()); + * {@literal @}Spy + * private ConsoleLoggingServiceImpl loggingService2; + * {@literal @}InjectMock + * private OrderService orderService; + * } + * + * + * @see net.jqwik.api.lifecycle.AddLifecycleHook + */ +public class MockitoLifecycleHooks implements AroundPropertyHook, AroundTryHook { + // type Object[] + private static final String MOCKS = "net.jqwik.mockito.mocks"; + + @Override + public PropagationMode propagateTo() { + return PropagationMode.ALL_DESCENDANTS; + } + + @Override + public PropertyExecutionResult aroundProperty(PropertyLifecycleContext context, PropertyExecutor propertyExecutor) + throws Throwable { + final List testInstances = context.testInstances(); + + // finds all mocked fields within the test instance object + MockitoSession session = null; + try { + final List mockList = new ArrayList<>(); + + final Optional actualStrictness = + context.findAnnotationsInContainer(MockitoSettings.class).stream() + .findFirst() + .map(MockitoSettings::strictness); + + session = Mockito.mockitoSession() + .initMocks(testInstances.toArray()) + .strictness(actualStrictness.orElse(null)) + .logger(new MockitoSessionLoggerAdapter(Plugins.getMockitoLogger())) + .startMocking(); + + for (final Object testInstance : testInstances) { + // open all the annotated mocks, keeping track of the handle so that we can close them later + // mockitoCloseables.add(MockitoAnnotations.openMocks(testInstance)); + // find all of the mocks in each of the test instances and store them in a list, so that we can reset + // them between tries. + mockList.addAll(MockFinder.getMocks(testInstance)); + } + + Store.create(MOCKS, Lifespan.PROPERTY, mockList::toArray); + + return propertyExecutor.execute(); + } finally { + if (session != null) { + session.finishMocking(); + } + } + } + + @Override + public TryExecutionResult aroundTry(TryLifecycleContext context, TryExecutor tryExecutor, List parameters) { + try { + return tryExecutor.execute(parameters); + } finally { + final Object[] mocks = Store.get(MOCKS).get(); + Mockito.reset(mocks); + } + } +} diff --git a/src/test/java/net/jqwik/mockito/AddingServiceTest.java b/src/test/java/net/jqwik/mockito/AddingServiceTest.java new file mode 100644 index 0000000..96fe668 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/AddingServiceTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito; + +import net.jqwik.api.Example; +import net.jqwik.api.ForAll; +import net.jqwik.api.Group; +import net.jqwik.api.Property; +import net.jqwik.api.lifecycle.AddLifecycleHook; +import net.jqwik.mockito.testcase.AddingService; +import net.jqwik.mockito.testcase.CountingService; +import org.mockito.InjectMocks; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@AddLifecycleHook(MockitoLifecycleHooks.class) +class AddingServiceTest { + private final CountingService countingService = mock(); + + @InjectMocks + private AddingService addingService; + + @Group + class BasicExamples { + @Example + void checkSingleValue() { + final String string1 = "a"; + final String string2 = "bb"; + + when(countingService.stringLength(string1)).thenReturn(1); + when(countingService.stringLength(string2)).thenReturn(2); + + final long result = addingService.addLengths(string1, string2); + assertThat(result).isEqualTo(3L); + verify(countingService).stringLength(string1); + verify(countingService).stringLength(string2); + } + } + + @Group + class PropertyTests { + @Property + void checkTotalLength2Args(@ForAll final String string1, @ForAll final String string2) { + when(countingService.stringLength(string1)).thenReturn(string1.length()); + when(countingService.stringLength(string2)).thenReturn(string2.length()); + + final long result = addingService.addLengths(string1, string2); + assertThat(result).isEqualTo((long) string1.length() + (long) string2.length()); + + verify(countingService, times(2)).stringLength(anyString()); + } + + @Property + void checkTotalLength4Args( + @ForAll final String string1, + @ForAll final String string2, + @ForAll final String string3, + @ForAll final String string4) { + when(countingService.stringLength(string1)).thenReturn(string1.length()); + when(countingService.stringLength(string2)).thenReturn(string2.length()); + when(countingService.stringLength(string3)).thenReturn(string3.length()); + when(countingService.stringLength(string4)).thenReturn(string4.length()); + + final long result1 = addingService.addLengths(string1, string2); + final long result2 = addingService.addLengths(string3, string4); + assertThat(result1).isEqualTo((long) string1.length() + (long) string2.length()); + assertThat(result2).isEqualTo((long) string3.length() + (long) string4.length()); + + verify(countingService, times(4)).stringLength(anyString()); + } + } +} diff --git a/src/test/java/net/jqwik/mockito/ProductServiceTest.java b/src/test/java/net/jqwik/mockito/ProductServiceTest.java new file mode 100644 index 0000000..c731945 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/ProductServiceTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito; + +import net.jqwik.api.ForAll; +import net.jqwik.api.Property; +import net.jqwik.api.lifecycle.AddLifecycleHook; +import net.jqwik.mockito.testcase.LoggingService; +import net.jqwik.mockito.testcase.Product; +import net.jqwik.mockito.testcase.ProductRepository; +import net.jqwik.mockito.testcase.ProductService; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@AddLifecycleHook(MockitoLifecycleHooks.class) +public class ProductServiceTest { + private static class ConsoleLoggingService implements LoggingService { + @Override + public void log(String x) { + System.out.println(x); + } + } + + @Mock + private ProductRepository productRepository; + + private final LoggingService loggingService = spy(new ConsoleLoggingService()); + + @Captor + private ArgumentCaptor loggingMessageCaptor; + + @InjectMocks + private ProductService productService; + + @Property + void verifyFindProduct(@ForAll int productId) { + final Product product = new Product(productId, "Product " + productId); + when(productRepository.getById(productId)).thenReturn(product); + + final Product result = productService.getProductById(productId); + + assertThat(result).isEqualTo(product); + verify(loggingService).log(loggingMessageCaptor.capture()); + verify(loggingService, times(1)).log(anyString()); + final String loggingMessageCaptorValue = loggingMessageCaptor.getValue(); + assertThat(loggingMessageCaptorValue).startsWith("Retrieved product for ID "); + assertThat(loggingMessageCaptorValue).endsWith(product.toString()); + } +} diff --git a/src/test/java/net/jqwik/mockito/testcase/AddingService.java b/src/test/java/net/jqwik/mockito/testcase/AddingService.java new file mode 100644 index 0000000..a04a450 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/AddingService.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +public class AddingService { + private final CountingService countingService; + + public AddingService(final CountingService countingService) { + this.countingService = countingService; + } + + public long addLengths(final String string1, final String string2) { + return countingService.stringLength(string1) + countingService.stringLength(string2); + } +} diff --git a/src/test/java/net/jqwik/mockito/testcase/CountingService.java b/src/test/java/net/jqwik/mockito/testcase/CountingService.java new file mode 100644 index 0000000..8d0c5c7 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/CountingService.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +public interface CountingService { + int stringLength(String input); +} diff --git a/src/test/java/net/jqwik/mockito/testcase/LoggingService.java b/src/test/java/net/jqwik/mockito/testcase/LoggingService.java new file mode 100644 index 0000000..0de40b1 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/LoggingService.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +public interface LoggingService { + void log(String message); +} diff --git a/src/test/java/net/jqwik/mockito/testcase/Product.java b/src/test/java/net/jqwik/mockito/testcase/Product.java new file mode 100644 index 0000000..0bbea6e --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/Product.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +@lombok.Value +public class Product { + int productId; + String description; +} diff --git a/src/test/java/net/jqwik/mockito/testcase/ProductRepository.java b/src/test/java/net/jqwik/mockito/testcase/ProductRepository.java new file mode 100644 index 0000000..2a8b6b1 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/ProductRepository.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +public interface ProductRepository { + Product getById(int productId); +} diff --git a/src/test/java/net/jqwik/mockito/testcase/ProductService.java b/src/test/java/net/jqwik/mockito/testcase/ProductService.java new file mode 100644 index 0000000..6db4b24 --- /dev/null +++ b/src/test/java/net/jqwik/mockito/testcase/ProductService.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Atlassian US, Inc + */ +package net.jqwik.mockito.testcase; + +public class ProductService { + private final ProductRepository productRepository; + private final LoggingService loggingService; + + public ProductService(ProductRepository productRepository, LoggingService loggingService) { + this.productRepository = productRepository; + this.loggingService = loggingService; + } + + public Product getProductById(final int productId) { + final Product product = productRepository.getById(productId); + loggingService.log(String.format("Retrieved product for ID %s: %s", productId, product)); + return product; + } +}
+ * import net.jqwik.api.lifecycle.AddLifecycleHook; + * import org.mockito.InjectMocks; + * import org.mockito.Mock; + * import org.mockito.Mockito; + * import org.mockito.Spy; + * {@literal @}AddLifecycleHook(MockitoLifecycleHooks.class) + * class OrderServiceTest { + * {@literal @}Mock + * private OrderRepository orderRepository; + * private OrderRepository orderRepository2 = Mockito.mock(); + * private LoggingService loggingService = Mockito.spy(new ConsoleLoggingServiceImpl()); + * {@literal @}Spy + * private ConsoleLoggingServiceImpl loggingService2; + * {@literal @}InjectMock + * private OrderService orderService; + * } + *