For some reason (I'm not very good with Spock and am new to mocking & stubbing) I can't get a particular method to stub with what I want.
I've shortened things down a bit, but basically I have a list of books returned from a search. Each book has a workId. There may be older or newer editions of the book, but they all have the same workId -- If a user chooses, they can grab all those other editions.
Preface
Class: Edition
class Edition {
//some strings, ints, and booleans -- information about the edition
}
Class: EditionResults for grabbing additional editions of a single work
class EditionResults {
List<Edition> editionsList
String parentWorkId
}
And one final class: BulkEditions for packaging multiple works (10 books on a search result page)
class BulkEditions {
List<EditionResults> bulkEditions
}
This controller is where I'm having trouble
#RequestMapping('/api')
class EditionsController {
#Autowired
private EditionsService editionsService
private Logger logger = LoggerFactory.getLogger(EditionsController)
#RequestMapping(value = '/bulkeditions', produces = 'application/json')
ResponseEntity<BulkEditions> getBulkEditions(
#RequestParam(value = 'workIds') Set<String> workIds
) throws Exception
{
BulkEditions newBulkEditions = new BulkEditions(bulkEditions: [])
workIds.each { workId ->
EditionResults editions = editionsService.searchEditions(workId)
logger.info "EditionResults {}", editions
if (editions?.editionsList) {
newBulkEditions.bulkEditions << editions
}
}
new ResponseEntity<BulkEditions>(newBulkEditions, HttpStatus.OK)
}
}
particularly, i'm having trouble testing the true branch of editions?.editionsList -- for some reason I keep receiving null for editions
Here's what my test looks like (it always passes, but clover coverage is failing)
class EditionsControllerSpec extends Specification {
private final EditionsService editionsService = Mock(EditionsService)
private static final Set<String> WORK_ID_SET = '12345' as Set<String>
private static final String SINGULAR_WORK_ID = '12345'
#Subject
private final EditionsController controller = new EditionsController(editionsService: editionsService)
void 'test get bulk editions'() {
given:
EditionResults editionResults = Mock(EditionResults)
Edition edition = GroovyMock(Edition) //it's a java class
edition.isAvailable >> true
//edition.bunchOfOtherProps >> randomValues
editionResults.editionsList >> [edition]
editionResults.parentWorkId >> SINGULAR_WORK_ID
editionsService.searchEditions(SINGULAR_WORK_ID) >> editionResults
when:
ResponseEntity<BulkLocalEditions> response = controller.getBulkEditions(WORK_ID_SET)
then:
response
}
My problem is this part:
editionsService.searchEditions(SINGULAR_WORK_ID) >> editionResults
I don't understand why I keep getting this back (from my logger)
[Test worker] INFO [EditionsController:100] [doCall] EditionResults ***null***
My understanding is that editionsService.searchEditions(SINGULAR_WORK_ID) >> editionResults is equivalent to saying "When we hit this searchEditions method, make its result editionResults" -- which we've already populated.... so how is it null?
I've tried being as detailed and clean as I can with the question, simplifying for brevity, but please let me know if there's any additional info needed -- and of course thanks in advance.
Also worth noting that it actually works on my local and everything comes back correctly -- it's just this clover coverage getting me
Thanks to #tim_yates
editionsService.searchEditions(SINGULAR_WORK_ID) >> editionResults should have been in my then block
Related
spring-data-cassandra version 3.4.6
spring-boot version 2.7.7
cassandra version 3.11
DataStax Java driver 3.10.0
We have a Controller method POST /all calling a service class with a transactional method inserting a Flux of simple versioned entities:
Controller
#PostMapping("/all")
#Operation(summary = "Create all data for the request account")
public Flux<SampleCassandraData> create(#RequestBody Flux<SampleCassandraData> datas,
RequestContext context) {
datas = datas.doOnNext(data -> data.setAccountId(context.getAccountId()));
return service.create(datas);
}
Service method
#Transactional
public Flux<SampleCassandraData> create(#NonNull Flux<SampleCassandraData> datas) {
return repository.insert(datas);
}
The entity:
#Table("sample_table")
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class SampleCassandraData {
#PrimaryKeyColumn(name = "account_id", type = PARTITIONED)
#EqualsAndHashCode.Include
private String accountId;
#PrimaryKeyColumn(name = "id", type = CLUSTERED, ordering = ASCENDING)
#EqualsAndHashCode.Include
private UUID id;
#Column("content")
private String content;
#Version
private Long version;
}
We have an integration test using cassandra test container inserting an element and then sending an array of 2 elements including the existing one. The expected response status is 409 CONFLICT with an OptimisticLockingFailureException in the logs and no data inserted.
#Test
void when_POST_all_with_duplicated_in_1st_then_CONFLICT() {
val existingData = given_single_created();
datas.set(0, existingData);
val newData = given_new_data();
datas.set(1, newData);
when_create_all(datas);
then_error_json_returned(response, CONFLICT);
then_data_not_saved(newData);
}
Test results:
HttpStatus is 409 CONFLICT as expected
Response json body is our error response as expected
Service logs show the OptimisticLockingFailureException stacktrace as expected
Test fails: newData was saved in Cassandra when we expected the transaction to be fully rollbacked.
What are we doing wrong?
Is there a configuration, annotation field we must set to enable full rollback?
Or is it the reactive nature and we can't expect full rollback?
Thanks in advance
In karate version 0.9.5 I was able to use System.setProperty('message', message) during a mock invocation. Then that property was available inside a feature using karate.properties['message']. I have upgraded to version 1.0.1 and now result of karate.properties['message'] results in undefined
Spock Test code
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock()
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
System.setProperty('foo', 'bar')
when:
Results results = Runner.path("classpath:").tags("~#ignore").parallel(5)
then:
results != null
1 * messageLogger.logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty("message", message)
}
}
}
Controller
#RestController
public class MessageController {
#Autowired private MessageLogger messageLogger;
#GetMapping("/message")
public String message() {
String message = "Important Message";
messageLogger.logMessage(message);
return message;
}
}
MessageLogger
#Component
public class MessageLogger {
public void logMessage(String message) {
System.out.println(message);
}
}
karate-config.js
function fn() {
karate.configure('connectTimeout', 10000);
karate.configure('readTimeout', 10000);
karate.configure('ssl', true);
var config = {
localUrl: 'http://localhost:' + java.lang.System.getProperty('server.port'),
};
print('localUrl::::::::::', config.localUrl);
return config;
}
Feature
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* print 'message value ' + karate.properties['message']
0.9.5
2021-04-28 15:07:51.819 (...) [print] **foo value bar**
2021-04-28 15:07:51.826 (...) [print] **message value Important Message**
1.0.1
2021-04-28 14:36:58.566 (...) [print] **foo value bar**
2021-04-28 14:36:58.580 (...) [print] **message value undefined**
Link to project on github
I cloned your project and noticed a few outdated things (Groovy, Spock and GMaven+ versions). Upgrading them did not change the outcome, I can still reproduce your problem.
A also noticed that in your two branches the POM differs in more than just the Karate version number, also the dependencies differ. If I use the ones from the 1.0.1 branch, tests do not work under 0.9.5 anymore. So I forked your project and sent you two pull requests for each branch with a dependency setup working identically for both Karate versions. Now the branches really just differ in the Karate version number:
https://github.com/kriegaex/spock-karate-example/compare/karate-0.9.5...kriegaex:karate-1.0.1
BTW, for some reason I had to compile your code running JDK 11, JDK 16 did not work. GMaven+ complained about Java 16 groovy class files (bytecode version 60.0), even though GMaven+ should have used target level 11. No idea what this is about. Anyway, on Java 11 I can reproduce your problem. As the Spock version is identical for both branches, I guess the problem is within Karate itself. I recommend to open an issue there, linking to your GitHub project (after you have accepted my PRs). Spock definitely sets the system property, I have added more log output into the stubbing closure order to verify that. Maybe this is an issue concerning how and when Karate communicates with Spock.
Update: Peter Thomas suggested in his answer to store the value to be transferred to the feature in a Java object and access that one from the feature after the Spock test has set it. I guess, he means something like this:
https://github.com/kriegaex/spock-karate-example/commit/ca88e3da
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
MessageHolder.INSTANCE.message = message
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
given:
Results results = Runner
.path("classpath:")
.systemProperty("foo", "bar")
.tags("~#ignore")
.parallel(5)
expect:
results
}
static class MessageHolder {
public static final MessageHolder INSTANCE = new MessageHolder()
private String message
private MessageHolder() {}
String getMessage() {
return message
}
void setMessage(String message) {
this.message = message
}
}
}
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage =
"""
function() {
var MessageHolder = Java.type('com.example.spock.karate.ApiTestRunnerSpec.MessageHolder');
return MessageHolder.INSTANCE.getMessage();
}
"""
* def message = call getMessage {}
* print 'message value ' + message
Update 2: This is the implementation of Peter's second idea to simply access Java system properties via JS. So I simplified the working, but unnecessarily complicated version with the message holder singleton, eliminating it again:
https://github.com/kriegaex/spock-karate-example/commit/e235dd71
Now it simply looks like this (similar to the original Spock specification, only refactored to be a bit less verbose):
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ApiTestRunnerSpec extends Specification {
#LocalServerPort
private int port
#SpringBean
MessageLogger messageLogger = Mock() {
1 * logMessage(_ as String) >> { String message ->
assert message != null
System.setProperty('message', message)
}
}
def "setup"() {
System.out.println("Running on port: " + port)
System.setProperty("server.port", "" + port)
}
def "Run Mock ApiTest"() {
expect:
Runner.path("classpath:").systemProperty("foo", "bar").tags("~#ignore").parallel(5)
}
}
The only important change is in the Karate feature:
#mockMessage
#parallel=true
Feature: Test Message
Background:
* url localUrl
Scenario: GET
Given path '/message'
When method get
Then status 200
* print 'foo value ' + karate.properties['foo']
* def getMessage = function() { return Java.type('java.lang.System').getProperty('message'); }
* print 'message value ' + getMessage()
The Runner "builder" has a .systemProperty() method which is recommended.
Please refer: https://github.com/intuit/karate/wiki/1.0-upgrade-guide#improved-test-suite-builder
So this should work. Else as I said in the comments, please submit a way to replicate.
Results results = Runner.path("classpath:")
.systemProperty("foo", "bar")
.tags("~#ignore").parallel(5)
EDIT: so I can confirm that karate.properties is made immutable at the time the test-suite starts.
So here are the 3 options:
change your test strategy so that all dynamic params are resolved before you start
instead of karate.properties[] do the old-school java.lang.System.getProperty('foo') call in Karate / JS, I'm pretty sure that will work
use a Java singleton to hold shared state for your test-runner and karate-feature
I am working on a Camunda java code and i am looking for a testing methodology that i can use to test any of my bpmn processes.
i have made some google search and i found on Camunda documentation some ideas about unit testing but it is do test for a specific bpmn model .
i need one to test any bpmn model (just by passing name of bpmn file and id of process etc)
the strategy should take into account the integration with DB to get candidate (user&group) for any expected path.i know maybe i can't do that but i have a large model and it will be time waste to test all of it in traditional ways.
Mohammad, your question is interesting - this goal can be achieved (test on bpmn can be dynamic), if we talk about not very detailed test.
Look at my code below, written by your idea of such common test (as i understood it, of course)
I use camunda-bpm-assert-scenario and camunda-bpm-assert libs in it.
First of all you gives to your test info about "wait states" in testing process (this can be done by json file - for not to change the code)
// optional - for mocking services for http-connector call, et.c.
private Map<String, Object> configs = withVariables(
"URL_TO_SERVICE", "http://mock-url.com/service"
);
private Map<String, Map<String, Object>> userTaskIdToResultVars = new LinkedHashMap<String, Map<String, Object>>() {{
put("user_task_0", withVariables(
"a", 0
));
put("user_task_1", withVariables(
"var0", "var0Value",
"var1", "var1Value"));
}};
// optional - if you want to check vars during process execution
private Map<String, Map<String, Object>> userTaskIdToAssertVars = new LinkedHashMap<String, Map<String, Object>>() {{
put("user_task_1", withVariables(
"a", 0
));
}};
Then you mocking user tasks (and other wait states) with given info:
#Mock
private ProcessScenario processScenario;
#Before
public void defineHappyScenario() {
MockitoAnnotations.initMocks(this);
for (String taskId : userTaskIdToResultVars.keySet()) {
when(processScenario.waitsAtUserTask(taskId)).thenReturn(
(task) -> {
// optional - if you want to check vars during process execution
Map<String, Object> varsToCheck = userTaskIdToAssertVars.get(taskId);
if (varsToCheck != null) {
assertThat(task.getProcessInstance())
.variables().containsAllEntriesOf(varsToCheck);
}
task.complete(userTaskIdToResultVars.get(taskId));
});
}
// If it needs, we describe mocks for other wait states in same way,
// when(processScenario.waitsAtSignalIntermediateCatchEvent(signalCatchId).thenReturn(...);
}
And your test will be anything like this:
#Test
#Deployment(resources = "diagram_2.bpmn")
public void testHappyPath() {
Scenario scenario = Scenario.run(processScenario).startByKey(PROCESS_DEFINITION_KEY, configs).execute();
ProcessInstance process = scenario.instance(processScenario);
assertThat(process).isStarted();
assertThat(process).hasPassedInOrder( // or check execution order of all elements -- not only wait-states (give them in additional file)
userTaskIdToResultVars.keySet().toArray(new String[0]) // user_task_0, user_task_1
);
assertThat(process).isEnded();
}
Hope this helps in your work.
I want to ask this question and I tried to search for a while without concrete answers.
I have made a database and used LINQ2SQL to auto-generate the classes needed.
I have set the serialization mode to unidirectional to make sure the classes are being serialized and making the datamembers.
Now, what I want to know is, how I can send the references to the other classes (which has been made through LINQ2SQL).
F.x. I have a Class called Scheduler which is referencing Reservation, and Seat, because Reservation and Seat have foreign keys.
You can see the dbml here:
http://imgur.com/rR6OxDi
The dbml file. This is the model of our database
Also you can see that when I run the WCF test client it does not return the objects of Seats and Reservation.
http://imgur.com/brxNBz7
Hopefully you can all help.
UPDATE
Here is the snippet of the code provided by LINQ2SQL.
This is the fields for the scheduler
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Scheduler")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Scheduler : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _SchID;
private System.Nullable<System.DateTime> _Date;
private System.Nullable<System.TimeSpan> _Starttime;
private System.Nullable<int> _MovieID;
private System.Nullable<int> _HallID;
private EntitySet<Seat> _Seats;
private EntitySet<Reservation> _Reservations;
private EntityRef<Hall> _Hall;
private EntityRef<Movie> _Movie;
private bool serializing;
And here is the snippet part of the code where it references to Reservation and Seat:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Seat", Storage="_Seats", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=6, EmitDefaultValue=false)]
public EntitySet<Seat> Seats
{
get
{
if ((this.serializing
&& (this._Seats.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Seats;
}
set
{
this._Seats.Assign(value);
}
}
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Reservations", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=7, EmitDefaultValue=false)]
public EntitySet<Reservation> Reservations
{
get
{
if ((this.serializing
&& (this._Reservations.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Reservations;
}
set
{
this._Reservations.Assign(value);
}
}
Update 2
Here is the Reservation class which LINQ2SQL made:
Here is the fields:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Reservation")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Reservation : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _ResID;
private System.Nullable<int> _CustomerID;
private System.Nullable<int> _SchedulerID;
private string _Row;
private string _Seat;
private EntityRef<Customer> _Customer;
private EntityRef<Scheduler> _Scheduler;
And here is the Scheduler reference part of the class
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Scheduler", ThisKey="SchedulerID", OtherKey="SchID", IsForeignKey=true, DeleteRule="SET DEFAULT")]
public Scheduler Scheduler
{
get
{
return this._Scheduler.Entity;
}
set
{
Scheduler previousValue = this._Scheduler.Entity;
if (((previousValue != value)
|| (this._Scheduler.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Scheduler.Entity = null;
previousValue.Reservations.Remove(this);
}
this._Scheduler.Entity = value;
if ((value != null))
{
value.Reservations.Add(this);
this._SchedulerID = value.SchID;
}
else
{
this._SchedulerID = default(Nullable<int>);
}
this.SendPropertyChanged("Scheduler");
}
}
}
All of these things should lead to where I could get the object like this:
Scheduler[] schedulers = client.GetAllSchedulers();
Reservation reservation = schedulers[0].Reservations.First();
But get this error due to WCF not sending the object, (which you could see in picture one).
Which is this error:
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Sequence contains
no elements
UPDATE 3:
Ok so it appears that it works somehow.
I just had to make a join between the Scheduler and Reservation.
Also whenever I debug the code I can see the variables are there. (Due to my reputation I can not post links).
But some of you might recognize the following whenever you try to view a result in debug mode:
"expanding the results view will enumerate the ienumerable c#"
Whenever I do this, it works, but not if I run it in release mode.
Looks like only object types (Reservation,Seat) have null values.
I'm guessing either you are missing DataContract/DataMember attributes in your complex types or you might need to include KnownTypeAttribute
It'd be easier to tell if you could provide some code.
EDIT
What your are talking about later is deferred loading. See this blog for more information on deferred vs immediate loading.
When you expand the IEnumerable in debug mode, that makes the request to retrieve/load the objects.
What your probably want is to load your Reservation,Seat objects along with your Scheduler object. Something like the following:
YourDatabaseContext database = new YourDatabaseContext ())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Scheduler>(sch=> sch.Reservation);
options.LoadWith<Scheduler>(sch=> sch.Seat);
database.LoadOptions = options;
}
See DataLoadOptions for more details.
If you want to understand deferred execution. See this article for more details.
Quote from the article:
By default LINQ uses deferred query execution. This means when you write a LINQ query it doesn't execute. LINQ queries execute when you 'touch' the query results. This means you can change the underlying collection and run the same query subsequent times in the same scope. Touching the data means accessing the results, for instance in a for loop or by using an aggregate operator like Average or AsParallel on the results.
I'm looking to write a test for a function that just returns a value - that's it. I'm not sure how you could do that. I'm under the impression you have to use system.assert or something. New to SFDC, but have programmed in many other languages. Here's some sample code:
static String getBrowserName()
{
String userAgent = ApexPages.currentPage().getHeaders().get('User-Agent');
if (userAgent.contains('iPhone'))
return 'iPhone-Safari';
if (userAgent.contains('Salesforce'))
return 'Salesforce';
if (userAgent.contains('BlackBerry'))
return 'BlackBerry';
if (userAgent.contains('Firefox'))
return 'Firefox';
if (userAgent.contains('Safari'))
return 'Safari';
if (userAgent.contains('internet explorer'))
return 'ie';
return 'other';
}
How can you obtain 100% test coverage for that?
While Salesforce's lack of a mocking framework is infuriating because of the hoops you have to jump through when testing things like page controllers, it's important to think about what you want to test here. Assuming that what you specifically want to test is that given the user agent strings your code returns the appropriate string, then I think something like the following should work:
static String getBrowserName(string userAgentStringToTest)
{
PageReference pageRef = getPageReference(userAgentStringToTest);
String userAgent = getUserAgent(pageRef);
...
}
PageReference getPageReference(string userAgentStringToTest)
{
if(userAgentStringToTest.Length == 0)
{
return ApexPages.currentPage();
}
else
{
PageReference pageRef = new PageReference('someURL');
pageRef.getHeaders().put('User-Agent', userAgentStringToTest);
return pageRef;
}
}
String getUserAgent(PageReference pageRef)
{
pageRef.getHeaders().get('User-Agent');
}
You would then call the getBrowserName method with the empty string in your production code and with the string you want to test in your test code.
There are a few different flavours to this of course - you could overload the methods and have a parameterless method for the main code and a parameterized method for testing. It's not ideal, but I don't know of another way to do it on the force.com platform currently.
EDIT: Just for completeness, I'm adding sample tests to clarify things. My example showed how to refactor the production code to make it testable, but did not give an example of how to write a test like the OP asked for.
Your tests would look something like this:
static testMethod void checkIPhoneBrowser()
{
String actualBrowserName = getBrowserName('string containing iPhone somewhere');
String expectedBrowserName = 'iPhone-Safari';
System.assertEquals(expectedBrowserName , actualBrowserName );
}
static testMethod void checkIEBrowser()
{
String actualBrowserName = getBrowserName('string containing internet explorer somewhere');
String expectedBrowserName = 'ie';
System.assertEquals(expectedBrowserName , actualBrowserName );
}
...