I want to use jmockit to test the static method in Spock, and combine the where tag to achieve different values of each mock to test different business logic. I tried a lot of writing methods, but they all failed. I hope I can get help or suggestions here. Thank you very much
Here is an example of my business code:
public class MyUtils {
public static int staticMethod(int origin) {
return 0;
}
}
public class MyClass {
public void verify(int origin) {
if (MyUtils.staticMethod(origin) == 1) {
System.out.println("1");
}
if (MyUtils.staticMethod(origin) == 2) {
System.out.println("2");
}
...
}
}
This is my Spock test codeļ¼
def "verify"() {
when:
myClass.verify(0)
then:
true
where:
mock | _
mockStatic(1) | _
mockStatic(2) | _
}
def mockStatic(val){
new MockUp<MyUtils>() {
#Mock
public int staticMethod(int origin) {
return val
}
}
}
I know that power can implement such a function, but because our team has been using jmockit, we want to know whether jmockit can implement such multiple different values of mock in Spock?
Put your method call into a closure and evaluate the closure during each iteration:
package de.scrum_master.stackoverflow.q67882559
import mockit.Mock
import mockit.MockUp
import mockit.internal.state.SavePoint
import spock.lang.Requires
import spock.lang.Specification
import spock.lang.Unroll
class StaticMethodJMockitTest extends Specification {
def jMockitSavePoint = new SavePoint()
def cleanup() {
jMockitSavePoint.rollback()
}
#Unroll
def "verify"() {
given:
mockClosure()
MyClass myClass = new MyClass()
when:
myClass.verify(0)
then:
true
where:
mockClosure << [
{ /* no mock */ },
{ mockStatic(1) },
{ mockStatic(2) }
]
}
def mockStatic(val) {
new MockUp<MyUtils>() {
#Mock
int staticMethod(int origin) {
return val
}
}
}
public static class MyUtils {
public static int staticMethod(int origin) {
return 0;
}
}
public static class MyClass {
public void verify(int origin) {
if (MyUtils.staticMethod(origin) == 1) {
System.out.println("1");
}
if (MyUtils.staticMethod(origin) == 2) {
System.out.println("2");
}
}
}
}
If you wish to use data tables, you need to help the parser a bit by explicitly adding it -> inside in the closure, if the closure is in the first column of the data table. You can also use some nice naming for your unrolled iterations:
#Unroll
def "verify #description"() {
given:
mockClosure()
MyClass myClass = new MyClass()
when:
myClass.verify(0)
then:
true
where:
description | mockClosure
"no mock" | { /* no mock */ }
"mock result 1" | { mockStatic(1) }
"mock result 2" | { mockStatic(2) }
}
The reason for creating and rolling back the save point is that JMockit does not play nice with Spock concerning mock lifecycles and the maintainer has no intention to even think about helping. See JMockit issue #668 for more info.
I have following command object with constraints :
#Validateable
class RefundCommand{
private static final Logger log = Logger.getLogger(RefundCommand.class)
Double amount
String order_id
MerchantAccount merchant
OrderReference order
String unique_request_id
public void getDerivedValues() {
this.order = OrderReference.findByOrderIdAndMerchantId(order_id, merchant.merchantId)
}
static constraints = {
amount nullable: false, validator: {amount, cmd->
if(cmd.amount <= 0) {
log.info("Amount must be greater than 0. Given value: ${cmd.amount}")
return ['invalid.amount']
}
}
}
}
Initiating an object in following way inside a controller:
def refund = {RefundCommand cmd->
def String orderId = params.orderId
def String merchantId = params.merchant.merchantId
def Double amount = params.amount.toDouble()
OrderReference orderReference = OrderReference.findByOrderIdAndMerchantId(orderId, merchantId)
MerchantAccount merchantAccount = MerchantAccount.findByMerchantId(merchantId)
cmd.order_id = orderId
cmd.merchant = merchantAccount
cmd.order = orderReference
cmd.amount = amount
cmd.unique_request_id = "rf_" + util.generateUniqueReference()
cmd.clearErrors()
cmd.validate()
log.info(cmd.dump())
if(cmd.hasErrors()) {
.....
return
}
proceedForRefund()
}
When I deploy initially validations are not working, validate() always return true and hasError() return null.
As we are using nginx, if I make any change in RefundCommand file then after auto-compile validation start working.
What could be the reason for that?
we are using grails-2.2.2 with nginx server.
I've three domain classess:
class Cafee {
String cafeeName
static hasMany = [halls: HallsZones]
static constraints = {
halls nullable: true
}
}
class HallsZones {
String hallName
static scaffold = true
static hasMany = [table : TablePlacesInfo]
static belongsTo = [cafee : Cafee]
static constraints = {
table nullable: true
cafee nullable: true
}
}
class TablePlacesInfo {
int placesInTableAmount
int tableAmount
int tableForReservationAmount
int placeCost
String currencyType
static scaffold = true
static belongsTo = [hall: HallsZones]
static constraints = {
hall nullable: true
}
}
As you can see, classess are connected with each other as via chain:
Cafee-(hasMany)->HallsZones-(hasMany)->TablePlacesInfo.
I want to get TablePlaces info, which has HallsZones as parent which in turn has a Cafee as parent.
I know how to search by parent, for example:
def table = TablePlacesInfo.findWhere(hall : params['hallsAvailable'], placesInTableAmount : Integer.parseInt(params['tablePlacesAvailable']))
But how to search by grandparent too?
Using where query:
TablePlacesInfo.where {
hall {
cafee {
// criteria matching grand parent
id == 1L // for example
}
}
}.list()
Using Criteria:
TablePlacesInfo.withCriteria {
hall {
cafee {
// criteria matching grand parent
idEq 1L // for example
}
}
}
Using hql:
TablePlacesInfo.executeQuery(
"""select tpi from TablePlacesInfo as tpi
inner join tpi.hall as hall
inner join hall.cafee as caf
where caf.id = 1"""
)
Choosing a DetachedCriteria or where would be a sound approach instead of dynamic finders.
I'm trying to figure out how to make a derived boolean field in my domain class. The boolean field is derived from comparison to the mapped owner's values. Is this possible in GORM? I've tried it quite a few different ways and I keep getting various SQL errors. My domain classes are below:
class Reading {
float readingValue
Date dateCreated
boolean alarmedState
static constraints = {
readingValue(nullable: false)
}
static belongsTo = [sensor : Sensor]
static mapping = {
autoTimestamp true
sort "dateCreated"
alarmedState formula: "(READING_VALUE < SENSOR.ALARM_IF_LESS) || (READING_VALUE > SENSOR.ALARM_IF_GREATER)"
}
}
class Sensor {
String description
String location
SensorType typeEnum
double alarmIfGreater
double alarmIfLess
static hasMany = [readings : Reading]
static constraints = {
alarmIfGreater(nullable: true)
alarmIfLess(nullable: true)
description(blank: false)
location(blank: false)
typeEnum(blank: false)
}
}
The transients property might do what you want. E.g.
class Reading {
float readingValue
Date dateCreated
static constraints = {
readingValue(nullable: false)
}
static belongsTo = [sensor : Sensor]
static transients = ['alarmedState']
static mapping = {
autoTimestamp true
sort "dateCreated"
//alarmedState formula: "(READING_VALUE < SENSOR.ALARM_IF_LESS) || (READING_VALUE > SENSOR.ALARM_IF_GREATER)"
}
Boolean getAlarmedState() {
( readingValue < sensor.alarmIfLess || readingValue > sensor.alarmIfGreater )
}
}
I've not tested this code but it might get you on the right track...
Recently I have started refactoring my Grails application, everything looked good until I got 'object references an unsaved transient instance' errors. While debugging I have found that id for one of my domain object is always null for every instance. Even for instances loaded from database.
I have found that composite id (which I use in that class) is known to cause problems, but it worked fine few days ago, since then, I have done a lot changes to this class but none of them should cause this kind of problems... at least I couldn't find change that could cause it.
class QuestionPriority implements Serializable{
static int maxPriority = 6
static int minPriority = 1
int priority = maxPriority
Date lastTestDate
Date nextTestDate = new Date()
static belongsTo = [question:Question, subscription:Subscription]
static constraints = {
nextTestDate(nullable: true)
lastTestDate(nullable: true)
}
static mapping = {
id composite: ['subscription', 'question']
version false
}
static QuestionPriority create(Subscription sub, Question question, boolean flush = false) {
assert sub != null
assert question != null
assert sub.id != null
assert question.id != null
QuestionPriority qp = new QuestionPriority(subscription: sub, question: question)
sub.addToPriorities(qp)
question.addToPriorities(qp)
assert (qp.save(flush: flush, insert: true, failOnError: true))
return qp
}
boolean equals(other) {
if (!(other instanceof QuestionPriority)) {
return false
}
other.subscription?.id == subscription?.id &&
other.getQuestionToAsk?.id == question?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (subscription) builder.append(subscription.id)
if (question) builder.append(question.id)
builder.toHashCode()
}
void markAsCorrect() {
priorityDown()
calculateNextTestDate()
}
void markAsWrong() {
priorityUp()
calculateNextTestDate()
}
String getQuestionToAsk() {
return question.question
}
String getExpectedAnswer() {
return question.answer
}
Date calculateNextTestDate() {
DailyTestMode mode = subscription.testMode
if (!lastTestDate) {
nextTestDate = new Date(0, 0, 0)
} else {
nextTestDate = mode.calculateNextAskDate(this)
}
return nextTestDate
}
static void delete(User user, Question q) {
QuestionPriority qp = get(user.id, q)
if (qp) {
qp.delete()
}
}
static QuestionPriority get(Subscription sub, Question question) {
assert sub != null
assert question != null
assert sub.id != null
assert question.id != null
QuestionPriority qp = find 'from QuestionPriority where subscription.id=:subId and question.id=:questionId',
[subId: sub.id, questionId: question.id]
if (!qp) {
qp = create(sub, question)
}
return qp
}
private void priorityUp() {
priority = Math.min(priority + 1, maxPriority)
lastTestDate = new Date()
calculateNextTestDate()
}
private void priorityDown() {
priority = Math.max(priority - 1, minPriority)
lastTestDate = new Date()
calculateNextTestDate()
}
}
I would really appreciate any help
-----EDIT-------
Question domain oject:
class Question {
String question
String answer
boolean deleted
static transients = [ 'deleted']
static belongsTo = [studyList: StudyList]
static hasMany = [priorities: QuestionPriority]
static constraints = {
question(blank:false)
answer(blank:false)
}
static mapping = {
priorities(cascade: 'all-delete-orphan')
}
void setAnswer(String a){
answer = a.trim()
}
String toString(){
return "${question} = ${answer}"
}
}
Subscription domain object
class Subscription implements Serializable {
boolean active = true
Integer rating = null
Date subscriptionStartDate = new Date()
String dailyTestModeLiteral
static belongsTo = [user:User, studyList:StudyList]
static hasMany = [priorities:QuestionPriority]
static constraints = {
priorities(nullable: true)
rating(nullable: true)
subscriptionStartDate(nullable: true)
}
static mapping = {
priorities(cascade: 'all-delete-orphan')
}
void setDailyTestMode(DailyTestMode mode){
dailyTestModeLiteral = mode.getModeLiteral()
for(QuestionPriority priority:priorities){
priority.calculateNextTestDate()
}
}
DailyTestMode getTestMode(){
return DailyTestMode.getMode(dailyTestModeLiteral)
}
public static Subscription create(User user, StudyList list, String testModeLiteral = NormalDailyTestStrategy.literal, boolean flush = false){
Subscription subscription = get(user.id, list.id)
if(!subscription){
withTransaction {
subscription = new Subscription(user: user, studyList: list, dailyTestModeLiteral: testModeLiteral)
user.addToSubscriptions(subscription)
list.addToSubscriptions(subscription)
subscription.save(failOnError: true, flush: true)
}
subscription.studyList.questions.each {
QuestionPriority.create(subscription, it)
}
}
return subscription
}
int getScore(){
float prioritySum = 0
float priorityMax = 0
float priorityMin = 0
priorities.each {
prioritySum += it.priority
priorityMin += 1
priorityMax += QuestionPriority.maxPriority
}
return Math.round(100 * (prioritySum-priorityMax)/(priorityMin - priorityMax))
}
public static void delete(User user, StudyList list){
Subscription subscription = get(user.id, list.id)
if(subscription){
subscription.delete()
}
}
static Subscription get(long userId, long studyListId) {
find 'from Subscription where user.id=:userId and studyList.id=:studyListId',
[userId: userId, studyListId: studyListId]
}
boolean equals(other) {
if (!(other instanceof Subscription)) {
return false
}
other.user?.id == user?.id &&
other.studyList?.id == studyList?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (studyList) builder.append(studyList.id)
builder.toHashCode()
}
boolean checkIfMatches(QuestionPriority questionPriority, String questionAsked, String answerGiven) {
return(questionPriority.question.question==questionAsked &&
questionPriority.question.answer == answerGiven)
}
QuestionPriority checkIfMatchesAny(String questionAsked, String answerGiven) {
Question q = Question.withCriteria {
and {
eq('question', questionAsked)
eq('answer', answerGiven)
eq('studyList', studyList)
}
}
if(q){
List<QuestionPriority> qp = QuestionPriority.withCriteria {
and {
eq('question', q)
eq('subscription', this)
}
}
if(qp.size()>0){
return qp[0]
}else{
return null
}
}
}
}
A few hours of debuging later I still know only that executing save(flush: true, failOnError: true) on QuestionPriority object returns unsaved object (no validation errors, no exceptions... no helpful information).
Any idea where to look or how to search for cause of this problem would be helpful, because I'm 100% stuck on this...
After hours of searching, changing framework to newest version and fixing new problems resulting from version change, I fixed this problem. It looks like changing composite ID into normal ID + composite unique key solved the problem. I couldn't find other solution, even thou my code worked fine with composite id for first couple of months... (any part of code related to composite id was changed when it stopped working) and I'm almost sure that I tried removing composite ID earlier and it didn't help then...