-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #78 from ppi-ag/feature-api-refactoring
Feature api refactoring Issue #45
- Loading branch information
Showing
26 changed files
with
1,003 additions
and
765 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
deepsampler-core/src/main/java/de/ppi/deepsampler/core/api/FunctionalSampleBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* Copyright 2020 PPI AG (Hamburg, Germany) | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package de.ppi.deepsampler.core.api; | ||
|
||
import de.ppi.deepsampler.core.model.Answer; | ||
import de.ppi.deepsampler.core.model.SampleDefinition; | ||
|
||
/** | ||
* <p> | ||
* Provides a fluent API for creating a {@link SampleDefinition} for methods that return a value (i.e. functions). | ||
* </p> | ||
* | ||
* <p> | ||
* With the FunctionalSampleBuilder you are able to define: | ||
* <ul> | ||
* <li>A concrete return value</li> | ||
* <li>An abstract {@link Answer} that executes arbitrary code and returns a value</li> | ||
* </ul> | ||
* <p> | ||
* The return value will be used (and evaluated) when the stubbed method will be invoked. | ||
* </p> | ||
* | ||
* @param <T> type of the class you want stub | ||
*/ | ||
public class FunctionalSampleBuilder<T> extends VoidSampleBuilder { | ||
|
||
/** | ||
* Create a {@link FunctionalSampleBuilder} with a sampler of the class you want to build a sample for, and the sampleDefinition | ||
* you want to extend. | ||
* | ||
* @param sampler the sampler {@link Sampler} | ||
* @param sampleDefinition {@link SampleDefinition} | ||
*/ | ||
@SuppressWarnings("unused") | ||
public FunctionalSampleBuilder(final T sampler, final SampleDefinition sampleDefinition) { | ||
super(sampleDefinition); | ||
} | ||
|
||
/** | ||
* Defines the value, that will be returned when the stubbed method is invoked. This value is called the "Sample". | ||
* | ||
* @param sampleReturnValue the return value that should be returned by the stubbed method. | ||
*/ | ||
public void is(final T sampleReturnValue) { | ||
getSampleDefinition().setAnswer(stubInvocation -> sampleReturnValue); | ||
} | ||
|
||
/** | ||
* In most cases it will be sufficient to define a fixed Sample as a return value for a stubbed method, but sometimes it | ||
* is necessary to execute some logic that would compute the return value or that would even change some additional state. | ||
* This can be done by using an Answer like so: | ||
* | ||
* <code> | ||
* Sample.of(sampler.echo(anyString())).answer(invocation -> invocation.getParameters().get(0)); | ||
* </code> | ||
* <p> | ||
* In essence using Answers gives free control on what a stubbed method should do. | ||
* | ||
* @param answer supplier you want to get evaluated when the stubbed method got invoked | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public void answers(final Answer<? extends Throwable> answer) { | ||
getSampleDefinition().setAnswer((Answer<Throwable>) answer); | ||
} | ||
|
||
} |
99 changes: 99 additions & 0 deletions
99
deepsampler-core/src/main/java/de/ppi/deepsampler/core/api/PersistentSample.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright 2020 PPI AG (Hamburg, Germany) | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package de.ppi.deepsampler.core.api; | ||
|
||
import de.ppi.deepsampler.core.error.NotASamplerException; | ||
import de.ppi.deepsampler.core.internal.ProxyFactory; | ||
import de.ppi.deepsampler.core.model.SampleDefinition; | ||
import de.ppi.deepsampler.core.model.SampleRepository; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* This is the starting point for the definition of Samples in test classes. | ||
* | ||
* A "Sample" is an exemplary return value or an exemplary behavior of a method that fulfills a prerequisite of a particular test case. When the tested methods are not able | ||
* to reproduce the Sample by their own means (i.e. due to changes in the underlying database, or passing of time, etc.), the methods can be coerced to reproduce the Sample using | ||
* this API. | ||
* | ||
* A method is called "sampled" if the method is coerced to return a Sample. | ||
* | ||
* Objects which contain sampled methods, are called "Sampler". | ||
* | ||
* DeepSampler defines Samples on classes rather then on objects. This enables DeepSampler to change the behavior of all instances of these classes without the need to instantiate | ||
* the Sampler manually and to distribute the Sampler into the objects that will be tested. The distribution is done by a Dependency Injection Framework like Spring or Guice. | ||
* | ||
* Methods of the class {@link Object} are ignored. Otherwise strange effects might appear, e.g. if Object::finalize is | ||
* called by the garbage collector. | ||
* | ||
* @author Jan Schankin, Rico Schrage | ||
*/ | ||
public class PersistentSample { | ||
|
||
private PersistentSample() { | ||
// This class is meant to be used as a frontend for a static fluent API and should never be instantiated. | ||
} | ||
|
||
|
||
/** | ||
* Defines a sampled method by calling the method inside of the parameter. The returned {@link FunctionalSampleBuilder} will then offer possibilities to define the Sample, | ||
* or in other words, it offers possibilities to override the default behavior or the return value of a method. | ||
* | ||
* @param sampledMethodCall The method call that will be sampled. | ||
* @param <T> The type of the return value and therefore the type of the Sample. | ||
* @return A {@link FunctionalSampleBuilder} which can be used to define the concrete Sample. <b>Do not</b> keep references to this object, it is intended to be used as a | ||
* fluent API only. | ||
*/ | ||
public static <T> PersistentSampleBuilder<T> of(final T sampledMethodCall) { | ||
SampleDefinition currentSampleDefinition = SampleRepository.getInstance().getCurrentSampleDefinition(); | ||
SampleDefinition lastSampleDefinition = SampleRepository.getInstance().getLastSampleDefinition(); | ||
|
||
if (currentSampleDefinition == lastSampleDefinition) { | ||
throw new NotASamplerException("sampledMethodCall is not a Sampler. Did you prepare the Sampler using Sampler.prepare() or @PrepareSampler?"); | ||
} | ||
|
||
currentSampleDefinition.setMarkedForPersistence(true); | ||
|
||
SampleRepository.getInstance().setLastSampleDefinition(currentSampleDefinition); | ||
return new PersistentSampleBuilder<>(sampledMethodCall, currentSampleDefinition); | ||
} | ||
|
||
/** | ||
* Along with the subsequent method call it defines a Sample for which the framework should start to track | ||
* how often this Sample is used in the component. This is necessary to be able to verify the invocation | ||
* of a specific method. | ||
* | ||
* @param sampler the sampler for which you want to activate a method call | ||
* @param <T> the type of the target Class/sampler | ||
* @return the sampler itself | ||
*/ | ||
public static <T> T forVerification(final T sampler) { | ||
Objects.requireNonNull(sampler); | ||
|
||
if (!ProxyFactory.isProxyClass(sampler.getClass())) { | ||
throw new NotASamplerException(sampler.getClass()); | ||
} | ||
|
||
SampleRepository.getInstance().setMarkNextVoidSamplerForPersistence(true); | ||
|
||
return sampler; | ||
} | ||
|
||
|
||
|
||
/** | ||
* This method will set the <code>sampleId</code> of the last defined sampleDefinition. Mostly you | ||
* want to set the sampleId with the Method {@link PersistentSampleBuilder#hasId(String)}. But in case of | ||
* void-returning methods, it is not possible to create a {@link FunctionalSampleBuilder}. As a consequence | ||
* you will need to set the id with this method. | ||
* | ||
* @param id the id you want to set. | ||
*/ | ||
public static void setIdToLastMethodCall(final String id) { | ||
SampleRepository.getInstance().getCurrentSampleDefinition().setSampleId(id); | ||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
deepsampler-core/src/main/java/de/ppi/deepsampler/core/api/PersistentSampleBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright 2020 PPI AG (Hamburg, Germany) | ||
* This program is made available under the terms of the MIT License. | ||
*/ | ||
|
||
package de.ppi.deepsampler.core.api; | ||
|
||
import de.ppi.deepsampler.core.model.SampleDefinition; | ||
|
||
/** | ||
* <p> | ||
* Provides a fluent API for creating a {@link SampleDefinition} for persistent Samples that are loaded from a file or any other DataSource. | ||
* The persistence (i.e. recording and loading samples) is managed by PersistentSampleManager. | ||
* </p> | ||
* | ||
* <p> | ||
* With the sampleBuilder you are able to define a sampleId, that is used to identify a Sample in the persistence. By default | ||
* DeepSampler creates a sampleId from the signature of the stubbed method. If this signature is changed, maybe because of a future | ||
* refactoring, the sample cannot be loaded from the persistence anymore. Defining manual sampleIds can be used to avoid this situation. | ||
* | ||
* </p> | ||
* | ||
* @param <T> type of the class you want stub | ||
*/ | ||
public class PersistentSampleBuilder<T> extends SampleBuilder { | ||
|
||
/** | ||
* Create a {@link PersistentSampleBuilder} with a sampler of the class you want to build a sample for, and the sampleDefinition | ||
* you want to extend. | ||
* | ||
* @param sampler the sampler {@link Sampler} | ||
* @param sampleDefinition {@link SampleDefinition} | ||
*/ | ||
@SuppressWarnings("unused") | ||
public PersistentSampleBuilder(final T sampler, final SampleDefinition sampleDefinition) { | ||
super(sampleDefinition); | ||
} | ||
|
||
/** | ||
* Set an id for the current SampleDefinition. The Id is used to find a Sample for a stubbed method. By default | ||
* sampleIds are generated from the signature of the sampled method. Therefore a change of the signature would mean, that | ||
* DeepSample isn't able anymore to find the Sample for the method. To prevent this situation, manual sampleIds can be used. | ||
* | ||
* @param sampleId the sampleId you want to set | ||
* @return this | ||
*/ | ||
public PersistentSampleBuilder<T> hasId(final String sampleId) { | ||
getSampleDefinition().setSampleId(sampleId); | ||
return this; | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.