I'm using geb and spock for functional tests in my Grails 2.3.7 app. The app allows real-time interaction between users, which I'd like to test automatically.
Does anyone know how, or if it's possible, to run two different browsers in one Spec? For example, see the pseudo code below for how I imagine this working in a perfect world.
#Stepwise
class ChatPageSpec extends GebReportingSpec {
def "login with chrome"() {
}
def "login with firefox"() {
}
def "send chat request with chrome"() {
}
def "accept chat request with firefox"() {
}
def "send hello with chrome"() {
}
def "receive hello with firefox"() {
}
}
Running the tests in parallel as Craig describes (http://www.objectpartners.com/2013/11/14/parallel-grails-functional-tests-with-geb-and-gradle/) looked promising, but if I understand it correctly, the parallel tests would use different app instances, which won't work. Plus, #Stepwise or something similar would have to keep the browsers in sync.
I also checked out the poly driver plugin (http://grails.org/plugin/poly-driver), but it looks like it's designed for one browser per Spec.
Ken
I'm afraid that you will need to handcraft something for this.
There is an example of achieving this which was once posted to Geb mailing list. I have never used it myself but the code looks reasonable. It feels like you'll want to use MultiBrowserGebSpec and override createBrowserMap().
You can create driver instances by hand and then set it on browsers instances using setDriver(). Another way would be to have different config files for different browsers which you would load using getConf() and pass to Browser constructor. Yet another one would be to have different environments for different browsers in one GebConfig.groovy and use different ConfifgurationLoaders created using the constructor that accepts environment name.
Related
I have a basic model like the following
class MyModel
def initialize(attrs)
#attrs = attrs
#rest_client = Some::REST::Client.new
end
def do_a_rest_call(some_str)
#rest_client.create_thing(some_str)
end
end
For testing purposes, I don't want #rest_client to make remote calls. Instead, in a test environment, I just want to make sure that #rest_client gets called with a specific some_str when it goes through certain branches of code.
In an ideal world, I'd have an assertion similar to:
expect(my_model_instance).to.receive(do_a_rest_call).with(some_str) where in the test I will pass some_str to make sure it's the right one.
What's the best way to do this using RSpec 3.8 and Rails 5.2.2?
A solution that should work without any additional gems:
let(:rest_client_double) { instance_double(Some::REST::Client, create_thing: response) }
it 'sends get request to the RestClient' do
allow(Some::REST::Client).to receive(:new).and_return(rest_client_double)
MyModel.new(attrs).do_a_rest_call(some_str)
expect(rest_client_duble).to have_received(:create_thing).with(some_str).once
end
Basically, you are creating a double for REST client.
Then, you make sure that when calling Some::REST::Client.new the double will be used (instead of real REST client instance).
Finally, you call a method on your model and check if double received given message.
(I think this question generalises to stubbing any extensively-pinged API, but I'm asking the question based on the code I'm actually working with)
We're using the Contentful Model extensively in our controllers and views including in our layouts. This means that in any feature test where we visit (say) the homepage, our controller action will include something like this:
class HomepageController < ApplicationController
def homepage
# ... other stuff
#homepage_content = Homepage.find ('contentful_entry_id')
end
end
... where Homepage is a subclass of ContentfulModel::Base, and #homepage_content will have various calls on it in the view (sometimes chained). In the footer there's a similar instance variable set and used repeatedly.
So for feature testing this is a pain. I've only come up with two options:
Stub every single call (dozens) on all Contentful model instances, and either stub method chains or ensure they return a suitable mock
or
Use a gem like VCR to store the Contentful responses for every feature spec
Both of these (at least the way I'm doing them) have pretty bad drawbacks:
1) leads to a bunch of test kruft that will have to be updated every time we add or remove a field from the relevant model;
2) means we generate a vcr yaml files for every feature test - and that we have to remember to clear the relevant yml file whenever we change an element of the test that would change the requests it sends
Am I missing a third option? Or is there some sensible way to do either of the above options without getting the main drawbacks?
I'm the maintainer of contentful_model.
We use VCR to stub API Calls, so that you can test with real data and avoid complicated test code.
Cheers
I started using Webflow on a Grails 2.2.4 project today and I immediately ran into the following exception:
could not initialize proxy - no Session
Doing some research I ran across this SO question which then led me to this JIRA issue that is marked as Won't Fix because making GORM calls in a TagLib is considered a bad idea.
Since this kind of stuff seems to work fine from regular actions, I've not run across the issue before. My question is, how should the following code be structured so that I'm following the right practice.
I have a template called _nav.gsp which handles my sites navigation bar. There are a lot of menu options that are shown and hidden depending on user's roles. I have the following in a TagLib:
def ifProjectGranted = { attr, body ->
def authenticatedUser = docRegService.authenticatedUser
if (authenticatedUser) {
def roles = UserRole.findAllByUser(authenticatedUser).collect { it.role.authority }
// here I check roles and manage my rendering to the output
}
}
I know I can solve the problem by wrapping my findAllByUser in a withTransaction block. But according to the JIRA, I shouldn't be doing this lookup at all here. So where would I do it?
Any user can create his/her own robot. A robot should be edited only by the creator or an administrator.
The next code works perfectly and it is an easy and simple solution:
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
class RobotController {
def springSecurityService
def edit() {
if (Robot.get(params.id).usuario.username == springSecurityService.authentication.name
|| SpringSecurityUtils.ifAnyGranted("ROL_ADMIN,ROL_SUPERADMIN"))
println "editing allowed"
else
println "editing denied"
}
}
But my teacher recommended me to secure the web using Config.groovy. The next code does not work:
grails.plugins.springsecurity.interceptUrlMap = [
'/index.gsp': ["isAuthenticated()"],
'/robot/edit/**': ["Robot.get(params.id).usuario.username == springSecurityService.authentication.name
|| hasAnyRole('ROL_ADMIN','ROL_SUPERADMIN')"],
'/robot/**': ["isAuthenticated()"]
]
It does not work because different reasons:
It is not possible to call the domain class Robot in Config.groovy
params.id has no sense in this place
The Java "or" (||) is not valid here. I tried other ways with not luck. The Groovy documentation is not clear for me.
Is possible to do it in Config.groovy? If not, the correct way would be using <sec:access> ... </sec:access> somehow?
I don't think you can do that kind of thing with plain Spring Security core.
Your teacher is right in the sense that you probably shouldn't implement the security in an ad-hoc manner, but you don't necessarily should do it in the Config.groovy -- that is a bit limiting.
You probably want to use the Spring Security ACL plugin, it adds some more domain classes and allows you to set up the access control with much more fine grained detail.
Check out the official docs. It may take a little while for you to learn it, but it is much better than rolling out your own ACL mechanism.
My app wants to send an email using the custom mailers. I went through the doc http://grails.org/Mail+from+Grails which I find pretty incomplete. I followed the steps mentioned in the alternative mailer, but I get an exception
NullPointerException occurred when processing request: [GET] url/sendEmail
Cannot invoke method sendNewEmail() on null object.
My controller looks like this
XXXMailer paMailer
paMailer.sendNewEmail()
The web page you linked to is merely a proposal which may have never been implemented and hasn't been updated in 3 years. The most popular way to to send email from a Grails app is with the mail plugin. The docs are comprehensive, and it's very easy to use.
XXXMailer seems to be a service, or any other automatically wired thing. So, you have to define it as field, and only then used it from an method, like:
class MyController {
XXXMailer paMailer //now it will be filled with real instance
def myAction() {
paMailer.sendNewEmail()
}
}