I am trying to write a unit test for one of my domain classes right now. I cheated, I know the code works by use of the code however... I want to make sure I have some automated tests in place.
I am getting the following error:
Test a sponsor(some.vendor.Vendor2Spec)
|
java.lang.NullPointerException: Cannot invoke method getLevel() on null object
at some.vendor.Vendor.getSponsorLevel(Vendor.groovy:111)
at some.vendor.Vendor2Spec.Test a sponsor(Vendor2Spec.groovy:29)
|Completed 1 unit test, 1 failed in 0m 3s
.................Tests FAILED
In the following code I have indicated my line numbers where they are being called out as errors.
My Vendor looks like:
class Vendor {
def sponsorService
SponsorLevel getSponsorLevel(){
return sponsorService.getLevel(this) // Line 111
}
}
And my test is setup as follows:
#TestFor(Vendor)
#TestMixin(GrailsUnitTestMixin)
class Vendor2Spec extends Specification{
#Shared
def sponsorService = new SponsorService()
def setup() {
}
def cleanup() {
}
void "Test a sponsor"(){
when: 'A Sponsor donates $5'
def vendor = new Vendor(cashDonation: 5, sponsorService: sponsorService)
then: 'Amount Owed should be $5'
vendor.getAmountDue().equals(new BigDecimal("5"))
vendor.getSponsorLevel() == SponsorLevel.DIAMOND // Line 29
when:"A Sponsor donates an item of value"
vendor = vendor = new Vendor(itemValue: 5)
then: 'Amount Due is $0'
vendor.getAmountDue().equals(new BigDecimal("0"))
vendor.sponsorLevel == SponsorLevel.DIAMOND
}
}
When I started out I was not newing off my sponsorService and it kept complaining about the nullness so... I tried mocking (might have been doing it wrong) but... I need to test the objects use of the service so... I don't think I need a mock.
The service looks like:
class SponsorService {
static transactional = false
def getLevel(Vendor vendor){
if(!vendor){
return null
}
BigDecimal sponsoringAmount = BigDecimal.ZERO
sponsoringAmount = sponsoringAmount.add(vendor.cashDonation ?: BigDecimal.ZERO)
sponsoringAmount = sponsoringAmount.add(vendor.itemValue ?: BigDecimal.ZERO)
return SponsorLevel.getSponsoringLevel(sponsoringAmount)
}
}
And the enum:
enum SponsorLevel {
PLATINUM("Platinum"),
GOLD("Gold"),
SILVER("Silver"),
BRONZE("Bronze"),
DIAMOND("Diamond")
final String label
private SponsorLevel(String label){
this.label = label
}
public static SponsorLevel getSponsoringLevel(BigDecimal sponsoringAmount){
if(!sponsoringAmount){
return null
}
if(sponsoringAmount.compareTo(new BigDecimal("3000")) >= 0){
return PLATINUM
}
if(sponsoringAmount.compareTo(new BigDecimal("2000")) >= 0){
return GOLD
}
if(sponsoringAmount.compareTo(new BigDecimal("1000")) >= 0){
return SILVER
}
if(sponsoringAmount.compareTo(new BigDecimal("500")) >= 0){
return BRONZE
}
if(sponsoringAmount.compareTo(new BigDecimal("1")) >= 0){
return DIAMOND
}
return null
}
}
Generally speaking, Service classes should be mocked when called from other classes for Unit Tests, otherwise, you'd want to write an Integration test.
Personally, if that is all your service is doing I'd just make that a method on the domain itself:
class Vendor {
def sponsorService
SponsorLevel getSponsorLevel(){
BigDecimal sponsoringAmount = BigDecimal.ZERO
sponsoringAmount = sponsoringAmount.add(this.cashDonation ?: BigDecimal.ZERO)
sponsoringAmount = sponsoringAmount.add(this.itemValue ?: BigDecimal.ZERO)
return SponsorLevel.getSponsoringLevel(sponsoringAmount)
}
}
Your service isn't transactional, your getLevel() isn't doing anything with the database, and the method is specific to your Vendor Domain. So to me, it makes more sense to make this a Domain method on Vendor. It simplifies your code and your test.
Related
For example:
class CutService {
String delegateToSelf(){
aMethod()
}
String aMethod(){
"real groovy value from CUT"
}
}
I've tried a variety of approaches, including:
#TestFor(CutService)
class CutServiceSpec extends Specification {
def expectedValue = "expected value"
void "test mocking CUT method using MockFor"() {
given:
MockFor mockCutService = new MockFor(CutService)
mockCutService.ignore.aMethod {expectedValue}
def cutServiceProxy = mockCutService.proxyDelegateInstance()
when:
String actualValue = null
mockCutService.use {
actualValue = cutServiceProxy.delegateToSelf()
}
then:
expectedValue == actualValue
}
}
Which gives:
| Failure: test mocking CUT method using MockFor(com...CutServiceSpec)
| junit.framework.AssertionFailedError: No more calls to 'delegateToSelf' expected at this point. End of demands.
at com...CutServiceSpec.test mocking CUT method using MockFor_closure4(CutServiceSpec.groovy:45)
at com...CutServiceSpec.test mocking CUT method using MockFor(CutServiceSpec.groovy:44)
Using metaClass appears to do what I want:
void "test mocking CUT method using metaClass"() {
given:
service.metaClass.aMethod = { expectedValue }
when:
String actualValue = service.delegateToSelf()
then:
expectedValue == actualValue
}
This test runs green.
In the below class, I just need RevShareFormula.withCriteria to return a result,
but getting the exception in resultTransformer() method.
Can anyone tell me how to Mock the below method so that i get some result from withCriteria
Here is the class:
class PartnerFinancialService {
def getPartnerPayeeRevenuShareDetails(long partnerPayeeId, def contextTypeCode) {
def partnerPayeesRevShareFormula = RevShareFormula.withCriteria {
resultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP)
createAlias('partnerRevShareConfig', 'partnerRevShareConfig')
createAlias('pricingModel', 'pricingModel')
createAlias('partnerRevShareConfig.revshareCategory', 'revshareCategory')
and {
eq("revshareCategory.payeeProfileId", partnerPayeeId)
eq("revshareCategory.referenceContextTypeCode", contextTypeCode)
isNull("partnerRevShareConfig.revshareValidToDate")
}
projections {
property("id", "formulaId")
property("pricingModel.id", "pricingModelId")
property("pricingModel.pricingName", "pricingName")
property("pricingModel.pricingType", "pricingType")
..
..
}
}
}
Here is the test class
#TestFor(PartnerFinancialService)
#Mock(RevShareFormula)
class PartnerFinancialServiceSpec extends Specification {
void "test getPartnerPayeeRevShareDetails"() {
def partnerPayeeRevShare = new PartnerRevShareConfig()
partnerPayeeRevShare.id = 1
def revShareModel = new PricingModel();
revShareModel.id = 1
def partnerPayeeRevShareFormula = new RevShareFormula();
partnerPayeeRevShareFormula.id=5
partnerPayeeRevShareFormula.pricingModel = revShareModel
partnerPayeeRevShareFormula.partnerRevShareConfig = partnerPayeeRevShare
partnerPayeeRevShareFormula.revshareFormula = "revshare*10"
partnerPayeeRevShareFormula.revshareTierHighValue = 0
partnerPayeeRevShareFormula.revshareTierLowValue= 0
RevShareFormula.metaClass.static.withCriteria = {partnerPayeeRevShareFormula}
when:
def result = service.getPartnerPayeeRevenuShareDetails(1,"PKG")
then:
//assert result.pricingModel.id == 1
println "Succesfully Fetched from DB"
}
}
Getting the following exception.
<testcase classname="com.orbitz.dat.partners.PartnerFinancialServiceSpec" name="test getPartnerPayeeRevShareDetails" time="0.039">
<error message="No signature of method: com.orbitz.dat.partners.PartnerFinancialService.resultTransformer() is applicable for argument types: (org.hibernate.transform.AliasToEntityMapResultTransformer) values: [org.hibernate.transform.AliasToEntityMapResultTransformer#3632aa4]" type="groovy.lang.MissingMethodException">groovy.lang.MissingMethodException: No signature of method: com.orbitz.dat.partners.PartnerFinancialService.resultTransformer() is applicable for argument types: (org.hibernate.transform.AliasToEntityMapResultTransformer) values: [org.hibernate.transform.AliasToEntityMapResultTransformer#3632aa4]
at com.orbitz.dat.partners.PartnerFinancialService.$tt__getPartnerPayeeRevenuShareDetails_closure24(PartnerFinancialService.groovy:39)
at grails.gorm.CriteriaBuilder.invokeClosureNode(CriteriaBuilder.java:1093)
at grails.gorm.CriteriaBuilder.invokeMethod(CriteriaBuilder.java:314)
at org.grails.datastore.gorm.GormStaticApi.withCriteria_closure11(GormStaticApi.groovy:304)
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:302)
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:37)
at org.grails.datastore.gorm.GormStaticApi.withCriteria(GormStaticApi.groovy:303)
at com.orbitz.dat.partners.PartnerFinancialService.$tt__getPartnerPayeeRevenuShareDetails(PartnerFinancialService.groovy:38)
at com.orbitz.dat.partners.PartnerFinancialServiceSpec.test getPartnerPayeeRevShareDetails(PartnerFinancialServiceSpec.groovy:71)
Use an integration test. Never test ORM code with unit tests. I know it's slower and the experience is less enjoyable, but you are fooling yourself if you think that you are actually testing something related to database queries with this test. You are testing the testing framework of grails (the in-memory GORM implementation)
I'm trying to unit test a method where an object with a composite key is being inserted into the database. Whenever I run my unit test for this, I get the following.
Failure: |
testTransaction(com.myapp.foo.TestServiceSpec)
|
Condition not satisfied:
Transaction.count() == 1
| |
0 false
at com.myapp.foo.TestServiceSpec.testTransaction(TestServiceSpec.groovy:166)
If I remove the composite key code and nothing else from my domain class, the test passes.
This is my Domain Class, Transaction.groovy:
class Transaction implements Serializable {
String timestamp
String source
String tableName
String fieldName
Integer changeNumber
String fieldValue
String objectId
static mapping = {
id composite: ["timestamp", "source", "tableName", "fieldName", "changeNumber"], generator: 'assigned'
}
boolean equals(other) {
if (!(other instanceof Transaction)) {
return false
}
other.timestamp == timestamp && other.source == source && other.id == id && other.tableName == tableName && other.fieldName == fieldName && other.changeNumber == changeNumber
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append timestamp
builder.append source
builder.append tableName
builder.append fieldName
builder.append changeNumber
builder.toHashCode()
}
}
This is the code that's being tested:
def response = [code: 'OK']
def transaction = new Transaction()
transaction.timestamp = (new Date()).format("yyyy-MM-dd HH:mm:ss.SSS")
transaction.source = "APP"
transaction.tableName = "TABLE_NAME"
transaction.fieldName = "FIELD_NAME"
transaction.fieldValue = "FIELD_VALUE"
transaction.objectId = "OBJECT_ID"
def changeNumber = Transaction.createCriteria().get {
eq("objid", currentTransaction.objid)
eq("tableName", currentTransaction.tableName)
projections {
max("changeNumber")
}
}
currentTransaction.changeNumber = (changeNumber ?: 0) + 1
if(!transaction.save()) {
transaction.errors.each {
println it
}
response = [code: 'error transaction', status: 500]
}
return response
And finally, here's my unit test code:
void testTransaction() {
when:
def response = testService.loadTransaction() // Creates transaction row
then:
assert response == [code: 'OK']
assert Transaction.count() == 1
}
The domain structure was defined by another party, and I can't change it in any way, so the composite key is a must. Unfortunately many classes in this app use composite keys, so if I can't unit test them, a lot of my code can't be covered by unit testing. Any info to get me going in the right direction would be great.
Don't use unit tests to test persistence.
Unit tests have a GORM implementation, but it isn't backed by a database, only a ConcurrentHashMap. It's pretty good, but it should only ever be used to avoid having to mock the persistence layer when unit testing other artifact types like controllers. If you want to test persistence, use a database.
Otherwise, you'll see funky issues like this, and similar issues like false negatives or worse - false positives where a test passes that shouldn't, leaving bugs in your "well-tested" code.
I have this piece of code in a controller:
def update = {
Map model = [:]
model.foo = params.foo
model.bar = params.bar
def result = ""
MyObject obj = MyObject.findWhere(bar:bar, foo:foo)
MyObjectService.updateObj(model,obj)
result = true
render result as JSON
}
And this simple unit test:
def 'controller update'() {
given:
controller.params.foo = foo
controller.params.bar = bar
MyObject obj = new MyObject(bar:bar, foo:foo)
mockDomain(MyObject,[obj])
when:
controller.update()
then:
1 * MyObject.findWhere(bar:bar, foo:foo) >> obj
1 * MyObjectService.updateObj(model,obj)
and:
def model = JSON.parse(controller.response.contentAsString)
model == true
where:
foo = "0"
bar = "1"
}
Now this is failing by and it is telling me that, "not static method findWhere is applicable..." for those arguments. That "MyObject" is just an orm class, and when I run that application everything seems to be working fine, but the test is failing.
My logic is this:
I want to count how many times the findWhere and updateObj methods are call and I am also mocking their response. So findWhere will return the object I already mocked, and pass it on to the service.
Any ideas why this is failing ?
For mocking static methods you should use Spock's GroovyStub class which introduced in v0.7.
I am using shiro security in my grail application.
Grails version : 2.2.1
shiro : 1.2.0
I have a problem in writing grails unit test case for the controller with filter enabled. When the test case run without filters then it is working fine, if it runs withFilters then it is failing for accessControl() method not found in the controller. I dont know how to make Shiro's api to be visible while running the test case.I referred shiro unit test case link http://shiro.apache.org/testing.html but I couldn't get any info regarding accessControl().I have given sample code how my classes and test case looks like
MyController.groovy
def create() {
// getting request parameters and validation
String emailId = params.emailId
def ret = myService.createUser(emailId)
return ret
}
MyControllerFilters.groovy
def filters = {
loginCheck(controller: 'user') {
before = {
//do some business checks
// Access control by convention.
accessControl() // This is a dynamic method injected by ShiroGrailsPlugin to FilterConfig, but this is not visible during the test case.
}
}
MyControllerTests.groovy
#TestFor(MyController)
#Mock(MyControllerFilters)
class MyControllerTests {
#Before
void setup() {
// initializing some variables
}
void testCreateUserWithFilter() {
request.accessAllowed = true
withFilters(action:"create") {
controller.create()
}
assert response.message == "success"
}
}
Try to use:
ControllerOrService.metaClass.method = {
return "" //what you need to be returned
}
Add parameters to closure if method take parameters:
ControllerOrService.metaClass.method = { def a, def b ->
return a + b
}
Don't forget to use full name of method when you mock them in that way.