Here is a simple controller
class UserController {
#Transactional
def update() {
try {
....
throw new Exception("test transaction rollback")
...
} catch( e ) {
transactionStatus.setRollbackOnly()
respond "{ "status":"error" }"
}
}
}
Here is a unit test spec
#TestFor(UserController)
class UserControllerSpec extends Specification {
when:''
controller.update()
then:'verify that transaction has been rolled back'
// it fails with missing property, whats the best way to check that it was rolled back ?
controller.transactionManager.transactionRolledBack == true
}
whats the best way to test that the transaction has been rolled back ?
After #BurtBeckwith suggestion tried an integration spec.
// Domain User.groovy
class User { String firstName; String lastName }
// UserController.groovy
class UserController {
static responseFormats = ['json', 'xml']
static allowedMethods = [update: 'PUT']
#Transactional
def update( UserCommand cmd ) {
User m = User.get(id)
m.firstName=cmd.firstName
m.lastName=cmd.lastName
m.save(failOnError:true,flush:true)
transactionStatus.setRollbackOnly()
}
}
class UserCommand { Long id; String firstName; String lastName }
// IntegrationSpec UserControllerIntegrationSpec.groovy
import grails.test.spock.IntegrationSpec
class UserControllerIntegrationSpec extends IntegrationSpec {
def controller
def sessionFactory
def setup() {
controller = new UserController()
}
void "test transaction rollback"() {
given:'a user'
User u = new User(firstName:'oldFirstName',lastName:'oldLastName')
u.save(flush:true,failOnError:true)
controller.request.method='PUT'
controller.request.contentType = "application/json"
controller.request.content = '{"id":u.id,"firstName":"newFirstName","lastName":"newLastName"}'.getBytes()
when:'an update that rollsback is called'
controller.update()
// clear so that we do not get data from cache.
sessionFactory.currentSession.flush()
sessionFactory.currentSession.clear()
then:'we should still see old data'
User u2 = User.get(u.id)
// The following fails, u.firstName has 'newFirstName' which is wrong on rollback
u2.firstName == 'oldFirstName'
u2.lastName == 'oldLastName'
}
}
// DataSource.groovy
test {
dataSource {
url = "jdbc:mysql://localhost/starter_app_test"
driverClassName = "com.mysql.jdbc.Driver"
pooled = true
properties {
...
defaultAutoCommit=false
}
}
}
Any ideas why in integration test data seems to get persisted even when we rollback ?
Related
I'm upgrading an inherited Grails 2 app to 3.3.10. We have controller methods that rely on Domain class validation to control logic flow, but we can't get DomainClass validation to work in ControllerUnitTests when running in Grails 3
For example,
Gift and RecipientAddress are DomainClasses and RecipientAddress.hasErrors() is used to validate against the RecipientAddress constraints.
def confirmAddress() {
Gift gift = Gift.get(params.giftId)
if (!gift) {
render(view: "index", model: [invalid: true])
return
}
recipientAddress = recipientAddressService.storeAddressInformation(params, gift)
if (recipientAddress.hasErrors()) {
render(view: "index", model: getAddressErrorModel(gift, recipientAddress))
return;
} else {
return [
recipientAddress : recipientAddress,
gift : gift
]
}
}
In the following test, when I debug the controller method it does everything expected but recipientAddress.hasErrors() always returns true and the test fails.
ie:
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController> {
#Shared
#AutoCleanup
SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"],
RecipientAddress, Gift)
def setup() {
controller.recipientAddressService = Mock(RecipientAddressService)
}
void "test RecipientAddress Bad PhoneNumber"() {
given:
RecipientAddress recipientAddress = new RecipientAddress(
phone: '123-456-789'
)
UnitTestDataFactory dataFactory = UnitTestDataFactory.getDataFactory()
Gift gift = dataFactory.getMockGift()
gift.save()
params.giftId = gift.id
when:
recipientAddress.validate()
controller.confirmAddress()
then:
recipientAddress.hasErrors()
recipientAddress.getErrors().getFieldError('phone')
1 * controller.recipientAddressService.storeAddressInformation(params, gift) >> recipientAddress
view == '/giftDetails/index'
}
}
Implementing DataTest ie: ...implements ControllerUnitTest<GiftDetailsController>, DataTest { fixes the DomainClass validation, but breaks the controllers ability to get the saved Gift.
Is there a way get DomainClass validation working in a ControllerUnit test?
Fix
Implemented DataTest with mockDomains and had to remove the custom dataStore.
#Transactional
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
// #Shared
// #AutoCleanup
// SimpleMapDatastore dataStore = new SimpleMapDatastore([ConnectionSource.DEFAULT, "reporting"], RecipientAddress, Gift)
void setupSpec() {
mockDomains RecipientAddress, Gift
}
....
Is there a way get DomainClass validation working in a ControllerUnit
test?
Yes.
You probably want something like this...
class GiftDetailsControllerTest extends Specification implements ControllerUnitTest<GiftDetailsController>, DataTest {
Class[] getDomainClassesToMock() {
[Gift]
}
// ...
}
I am unit testing a Grails controller method that internally creates an user instance. User domain class uses the springSecurityService of the Spring Security plugin to encode the password before inserting it into the database.
Is there a way to mock that springSecurityService from my unit test in order to get rid of that error?
Failure: Create new individual member(MemberControllerSpec)
| java.lang.NullPointerException: Cannot invoke method encodePassword() on null object
Please find my unit test below.
#TestMixin(HibernateTestMixin)
#TestFor(MemberController)
#Domain([User, IndividualPerson])
class MemberControllerSpec extends Specification {
void "Create new individual member"() {
given:
UserDetailsService userDetailsService = Mock(UserDetailsService)
controller.userDetailsService = userDetailsService
def command = new IndividualPersonCommand()
command.username = 'scott#tiger.org'
command.password = 'What ever'
command.firstname = 'Scott'
command.lastname = 'Tiger'
command.dob = new Date()
command.email = command.username
command.phone = '89348'
command.street = 'A Street'
command.housenumber = '2'
command.postcode = '8888'
command.city = 'A City'
when:
request.method = 'POST'
controller.updateIndividualInstance(command)
then:
view == 'createInstance'
and:
1 * userDetailsService.loadUserByUsername(command.username) >> null
and:
IndividualPerson.count() == 1
and:
User.count() == 1
cleanup:
IndividualPerson.findAll()*.delete()
User.findAll()*.delete()
}
}
One way to mock a service is to use Groovy's MetaClass
import grails.test.mixin.Mock
import grails.plugin.springsecurity.SpringSecurityService
...
#Mock(SpringSecurityService)
class MemberControllerSpec extends Specification {
def setupSpec() {
SpringSecurityService.metaClass.encodePassword = { password -> password }
}
def cleanupSpec() {
SpringSecurityService.metaClass = null
}
....
In this example, the call to SpringSecurityService.encodePassword() will simply return the password in plain text.
An approach using Mocks is discussed here.
You can to use this code to encode password in User:
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
When springSecurityService is null, encodePassword is not called and NPE is not raised
When you use controller unit test with spring security rest plugin in Grails v4/v3, if your controller method reference springSecurityService methods like 'athenticatedUser', there will be NullPointException, because springSecurityService is not autowired into the spring application context.
Add code like below, you can inject springSecurityService and mock it's methods.
class GuessControllerSpec extends Specification implements ControllerUnitTest<GuessController> {
#Override
Closure doWithSpring() {
return {
// mock method
SpringSecurityService.metaClass.getCurrentUser = {return new User()}
// inject into spring context
springSecurityService(SpringSecurityService)
}
}
...
}
Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?
class BulkMessage {
List messages
}
class MyService {
def processBulkMessage(BulkMessage msg) {
msg.messages.each {subMsg->
processMessage(subMsg)
}
}
def processMessage(Message message) {
}
}
You can use spies and partial mocks (requires Spock 0.7 or newer).
After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:
def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"
Sometimes, it is desirable to both execute some code and delegate to the real method:
subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.
Here's how I would do it. I know how visibility works in groovy so mind the comments.
#Grab('org.spockframework:spock-core:0.7-groovy-2.0')
#Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class MessageServiceSpec extends Specification {
def 'test'() {
given:
def service = new MessageService()
def sender = GroovyMock(MessageSender)
and:
service.sender = sender
when:
service.sendMessages(['1','2','3'])
then:
3 * sender.sendMessage(_)
}
}
class MessageSender { //package access - low level
def sendMessage(String message) {
//whatever
}
}
class MessageService {
MessageSender sender //package access - low level
def sendMessages(Iterable<String> messages) {
messages.each { m -> sender.sendMessage(m) }
}
}
It does not use Spock built-in Mocking API (not sure how to partially mock an object), but this should do the trick:
class FooSpec extends Specification {
void "Test message processing"() {
given: "A Bulk Message"
BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])
when: "Service is called"
def processMessageCount = 0
MyService.metaClass.processMessage { message -> processMessageCount++ }
def service = new MyService()
service.processBulkMessage(bulk)
then: "Each message is processed separately"
processMessageCount == bulk.messages.size()
}
}
For Java Spring folks testing in Spock:
constructorArgs is the way to go, but use constructor injection. Spy() will not let you set autowired fields directly.
// **Java Spring**
class A {
private ARepository aRepository;
#Autowire
public A(aRepository aRepository){
this.aRepository = aRepository;
}
public String getOne(String id) {
tryStubMe(id) // STUBBED. WILL RETURN "XXX"
...
}
public String tryStubMe(String id) {
return aRepository.findOne(id)
}
public void tryStubVoid(String id) {
aRepository.findOne(id)
}
}
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository])
when:
a.getOne()
then:
// Stub tryStubMe() on a spy
// Make it return "XXX"
// Verify it was called once
1 * a.tryStubMe("1") >> "XXX"
}
}
Spock - stubbing void method on Spy object
// **Groovy Spock**
class ATest extends Specification {
def 'lets stub that sucker' {
setup:
ARepository aRepository = Mock()
A a = Spy(A, constructorArgs: [aRepository]) {
1 * tryStubVoid(_) >> {}
}
when:
...
then:
...
}
}
I'm using Grails 2.3.8 and trying to create a unit test for a filter that uses a service.
The filter:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
The spec, first attempt:
#TestFor(ExecutionTraceController)
#Mock(LicenseFilters)
class LicenseFiltersSpec extends Specification{
void "Test filter redirects when license is wrong"() {
given:
LicenseFilters bean=applicationContext.getBean("com.nortia.sgmentia.license.LicenseFilters")
bean.licenseService=this.buildLicenseServiceStub(false)
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok){
LicenseService result=Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
But it turns out (by debugging) that the bean that I grab from the context it is NOT the same one that receives the request thus I still get a NPE.
In a second attempt I try using defineBeans:
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean){
targetObject = this
targetMethod= "buildLicenseServiceStub"
arguments=[false]
}
}
when:
withFilters(action:"list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
But the mocked bean is neither bean instanciated nor inyected.
Should I try to inyect the service manually into the filter??
There was this issue https://jira.grails.org/browse/GRAILS-8976 but it is closed.
I came across a similar situation and was able to fix it by adding the service to the #Mock annotation, i.e. #Mock([LicenseFilters, LicenseService]).
In your case the spec would look something like the following:
#TestFor(ExecutionTraceController)
#Mock([LicenseFilters, LicenseService])
class LicenseFiltersSpec extends Specification {
void "Test filter redirects when license is wrong"() {
given:
defineBeans {
licenseService(MethodInvokingFactoryBean) {
targetObject = this
targetMethod = "buildLicenseServiceStub"
arguments = [false]
}
}
when:
withFilters(action: "list") {
controller.list()
}
then:
response.redirectedUrl == '/licenseExpired'
}
private LicenseService buildLicenseServiceStub(boolean ok) {
LicenseService result = Stub(LicenseService)
result.checkLicense() >> ok
return result
}
}
Note: that mocking the service in this manner will, by default, inject an instance of the actual LicenseService into your filter. So, if the above defineBeans block is removed the actual implementation of LicenseService.checkLicense() will be called.
I finally found a workaround to make it work going with the second approach (using defineBeans).
The service is not being autowired into the filter so I finally did it manually with a pseudo-singleton:
class LicenseFilters {
def licenseService
def filters = {
all(controller:'*', action:'*') {
before = {
if(!this.licenseService){
this.licenseService=applicationContext.getBean("licenseService")
}
if(!this.licenseService.checkLicense()){
redirect(controller:"licenseExpired")
return false
}
}
}
}
}
Quite ugly but a solution at least.
Hope it helps someone out there.
I'm writing controller unit tests and I'd like to test json result when creation fails.
How can I register VndErrorJsonRenderer in unit test ? I tried simply defineBeans in setup() but it doesn't work :(
import com.vividsolutions.jts.geom.Coordinate
import grails.transaction.Transactional
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
import static org.springframework.http.HttpStatus.CREATED
import static org.springframework.http.HttpStatus.NO_CONTENT
#Transactional(readOnly = true)
class UserController {
static namespace = "v1"
static allowedMethods = [profile: 'GET', create: "POST", update: "PUT", delete: "DELETE"]
static responseFormats = ['json', 'vnd.error+json']
def springSecurityService
def geometryFactory
/**
* Saves a resource
*/
#Transactional
def create() {
User instance = createResource(params)
instance.validate()
if (instance.hasErrors()) {
respond instance.errors, view: 'create' // STATUS CODE 422
return
}
instance.save flush: true
respond instance, [status: CREATED]
}
protected User createResource(GrailsParameterMap params) {
Double x = params.double("location.x", 0)
Double y = params.double("location.y", 0)
User user = new User()
bindData(user, params, [include: ['username', 'password', 'profile.*']])
if (x > 0 && y > 0)
user.location = geometryFactory.createPoint(new Coordinate(x, y))
else
user.location = null
user.roles = []
user.roles.add(Role.findByAuthority(Role.ROLE_USER))
return user
}
}
And my test :
#Before
void setup() {
defineBeans {
vndJsonErrorRenderer(VndErrorJsonRenderer)
}
}
void "Test the create action with a non unique username"() {
User.metaClass.encodePassword = {
"aaa"
}
// Create first user
assertNotNull getValidUser().save(flush: true)
when: "The create action is executed with a username already used"
def user = getValidUser()
controller.request.addHeader("Accept", "application/vnd.error+json,application/json")
controller.request.contentType = "application/json"
controller.request.content = JsonMapperUtil.mapAsJson(user)?.getBytes()
controller.create()
then: "The response status is UNPROCESSABLE_ENTITY and the username unique error is returned"
println response.text
response.status == UNPROCESSABLE_ENTITY.value
def json = JSON.parse(response.text)
assertNull "VND format not returned", json.errors
}
I'm using grails 2.3.6 with restful controller.
Thanks
In the case you are showing where you depend on respond it would be best to test this more as an integration test so all components that may interact with respond are all wired for you.
In a unit test for what ever beans are needed in the class under test I find it easiest to directly set them on the class under test.