I am trying to use getters and return a status in my array data
So in my controller i have
async index ({ request, response, view }) {
const users = await User.query()
.paginate(request.input("page"),request.input("perPage"))
return response.send(users);
}
In my User model i have added
class User extends Model{
getStatus (status_id) {
return status_id?"active":"Inactive";
}
}
But when i check my data returned in the index function above it doesnt contain the status of either active or Inactive.What am i missing out?
Getters always write get and then camel case like getStatusId
you write this
class User extends Model{
getStatusId(status_id) {
return status_id?"active":"Inactive";
}
}
More About Getter visit adonis official doc
Related
NodeEntity:
#NodeEntity(label = "User")
public class UserNode {
#GraphId
private Long _gid;
#Index(unique = true, primary = true)
#Convert(ObjectIdConverter.class)
private ObjectId id;
}
Converter:
public class ObjectIdConverter implements AttributeConverter<ObjectId, String>{
#Override
public String toGraphProperty(ObjectId value) {
return ObjectIdUtils.compressed(value);
}
#Override
public ObjectId toEntityAttribute(String value) {
return ObjectIdUtils.uncompress(value);
}
}
Repository:
public interface UserNodeRepository extends GraphRepository<UserNode> {
#Query("MATCH (user:User) WHERE user.id IN {0} RETURN user")
List<UserNode> findByIdIn(List<ObjectId> ids);
}
UserNodeRepository#findByIdIn is custom query. but the request parameter directly convert to json without using ObjectIdConverter.
Log:
o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (user:User) WHERE user.id IN {0} RETURN user with params {0=[{timestamp=1500442853, machineIdentifier=11302907, processIdentifier=11906, counter=4709865, time=1500442853000, date=1500442853000, timeSecond=1500442853}, {timestamp=1500445335, machineIdentifier=11302907, processIdentifier=11906, counter=4709946, time=1500445335000, date=1500445335000, timeSecond=1500445335}, {timestamp=1500447522, machineIdentifier=11302907, processIdentifier=11906, counter=4710014, time=1500447522000, date=1500447522000, timeSecond=1500447522}, {timestamp=1500448399, machineIdentifier=11302907, processIdentifier=11906, counter=4710092, time=1500448399000, date=1500448399000, timeSecond=1500448399}]}
Is this the SDN expectation or what concepts have I lost?
This is not possible for finders with custom #Query. There is no way for OGM to know that your parameter relates to a property with #Convert annotation (the method name is not used at all). To solve this convert to your property type manually instead and use that as a parameter:
#Query("MATCH (user:User) WHERE user.id IN {0} RETURN user")
List<UserNode> findByIdIn(List<String> ids);
However this works fine for derived finders - OGM knows the field, and the #Convert annotation with the right converter, from the method name.
Example with single parameter:
List<UserNode> findById(ObjectId id);
Unfortunately there seems to be a bug for the IN operator and a collection parameter with:
// doesn't work
List<UserNode> findByIdIn(List<ObjectId> ids);
I have created a jira issue.
I'm using Grails 2.3.7. I have a simple Person domain object:
class Person {
String name
static constraints = {}
}
And controller:
#Transactional(readOnly = true)
class PersonController {
def index() {
render view:'index', model:[personList:Person.list()]
}
def show(Long id) {
def person = Person.get(id)
render view:'show', model:[person:person]
}
...
}
I'm trying to write a unit test for the controller to exercise the show method, but I am not sure how I need to configure the test to ensure that the static call Person.get(id) returns a value.
Here's my (failing) attempt:
import grails.test.mixin.*
import spock.lang.*
#TestFor(PersonController)
#Mock(Person)
class PersonControllerSpec extends Specification {
void "Test show action loads Person by id and includes that person in the model rendered"() {
setup:
def personMockControl = mockFor(Person)
personMockControl.demand.static.get() { int id -> new Person(name:"John") }
when:"A valid person id passed to the show action"
controller.show(123)
then:"A model is populated containing the person domain instance loaded by that unique id"
model.person != null
model.person.name == "John"
}
}
This test fails because the condition model.person != null fails. How do I rewrite this test in order to make it pass?
EDIT
In this case I can side-step the static method call by reworking the test thusly:
void "Test show action loads Person by id and includes that person in the model rendered"() {
setup:
def p = new Person(name:"John").save()
when:"A valid person id passed to the show action"
controller.show(p.id)
then:"A model is populated containing the person domain instance loaded by that unique id"
model.person != null
model.person.id == p.id
model.person.name == "John"
}
However, I'd really like to understand how to correctly mock the Person's static get method.
EDIT 2
Here's another situation to demonstrate my perceived need to mock the get method. Given this filter code:
def fitlers = {
buyFilter(controller:"paypal", action:"buy") {
after = {
new VendorPayment(payment:request.payment, vendor: Vendor.get(params.buyerId)).save()
}
}
}
I'm trying to test that this filter works as expected using the following test:
void "test the buyFilter to ensure it creates a VendorPayment when the PayPal plugin controller is called" () {
setup:
// how do I mock the Vendor.get(params.buyerId)
// to return a vendor in order to save a new VendorPayment
when:
withFilters(action:"buy") {
controller.buy()
}
then:
VendorPayment.count() == 1
}
Like dmahaptro mentioned, you do not need to mock your person. The preferred/proper way of grabbing an object in a test via .get() is the way you have it--you create the domain instance, and pass its id to the controller action for consumption.
However, if you need to mock a static method like this there are two approaches:
1) Use the mockFor method provided by GrailsUnitTestMixin and then specify the static method like so:
GrailsMock mockPerson = mockFor(Person)
mockPerson.demand.static.get() { Long id -> new Person(name:"John") }
Note that in your case, the mock's static get method did not contain the correct parameter type (int was used instead of Long) so this method was not being invoked.
2) Modify the class' metaClass. Changing the metaClass is highly discouraged (especially when the #Mock annotation already sets everything up for you), and can lead to test leakage (i.e., other tests will not work properly after you've changed the metaClass, unless you restore the original metaclass after the test is done).
That said, for the sake of familiarity, the way you would mock the .get() method is via:
Person.metaClass.static.get = { Long id -> return personWithId }
Again, this is considered bad practice and should be avoided, but there you have it.
say I have two models that extend from Eloquent and they relate to each other. Can I mock the relationship?
ie:
class Track extends Eloquent {
public function courses()
{
return $this->hasMany('Course');
}
}
class Course extends Eloquent {
public function track()
{
return $this->belongsTo('Track');
}
}
in MyTest, I want to create a mock of course, and return an instance of track, by calling the track property, not the track instance (I don't want the query builder)
use \Mockery as m;
class MyTest extends TestCase {
public function setUp()
{
$track = new Track(array('title' => 'foo'));
$course = m::mock('Course[track]', array('track' => $track));
$track = $course->track // <-- This should return my track object
}
}
Since track is a property and not a method, when creating the mock you will need to override the setAttribute and getAttribute methods of the model. Below is a solution that will let you set up an expectation for the property you're looking for:
$track = new Track(array('title' => 'foo'));
$course = m::mock('Course[setAttribute,getAttribute]');
// You don't really care what's returned from setAttribute
$course->shouldReceive('setAttribute');
// But tell getAttribute to return $track whenever 'track' is passed in
$course->shouldReceive('getAttribute')->with('track')->andReturn($track);
You don't need to specify the track method when mocking the Course object, unless you are also wanting to test code that relies on the query builder. If this is the case, then you can mock the track method like this:
// This is just a bare mock object that will return your track back
// whenever you ask for anything. Replace 'get' with whatever method
// your code uses to access the relationship (e.g. 'first')
$relationship = m::mock();
$relationship->shouldReceive('get')->andReturn([ $track ]);
$course = m::mock('Course[track]');
$course->shouldReceive('track')->andReturn($relationship);
I am trying to write unit test case for grails controller which has following structure:
class MyController{
def save(){
def myDomain = new MyDomain(params)
business validation 1
business validation 2
myDomain.writedatasource.save()
business validation 3
business validation 4
}
}
Since Unit test doesn't load Datasource.groovy the writedatasource isn't available during unit testing so the test cases for "business validation 3" and "business validation 4" fail as I get
groovy.lang.MissingPropertyException: No such property: writedatasource for class: MyDomain
How can I modify my test case to test validation scenarios 3 and 4?
Test case is simple and looks like follows:
void testSave(){
...setup...
controller.save()
assert conditions
....
}
Not sure if that could make the trick, but you can try:
def 'the controller call the save method in writedatasource'() {
given:
def calls = 0
and:
def myDomainInstance = new MyDomain()
and:
MyDomain.metaClass.getWritedatasource = { new Object() }
and:
Object.metaClass.save = { Map attrs ->
calls++
}
when:
controller.save()
then:
calls == 1
}
But the only thing are you doing is testing that the save is called under writedatasource, so it would be better to have also an integration test. If that makes the trick, please answer as well at http://grails.1312388.n4.nabble.com/Mocking-in-a-unit-test-a-domain-object-multiple-datasources-td4646054.html as they seem to have the same problem.
Addressing original question:
I ran into this and would have had to mock many, many more methods than just 'save' on multiple domains. So instead, I overrode the getter of my domains to simply return the invoking domain instance:
def setup() {
[MyDomain1, MyDomain2].each { Class clazz ->
mockDomain(clazz)
clazz.metaClass.getWritedatasource = {
return delegate
}
clazz.metaClass.'static'.getWritedatasource = {
return delegate
}
}
}
This also saves me from including #DirtiesRuntime since I'm not updating the metaClass of anything I'd want to clean.
Most importantly, whatever calls the datasource, be it the domain class or the instance, it should be decorated with GORM fanciness from mockDomain, meaning I don't have to mock any of the GORM methods.
What if you want dynamic datasources?
In my particular case, datasources are configurable and may change depending on environment. If you're in a similar situation, you can configure this in your domain mapping:
static mapping = {
datasources Holders.grailsApplication?.config.dynamic?.datasources
...
}
where dynamic.datasources is an array of datasource names. Then, in the test setup:
def setup() {
grailsApplication.config.dynamic.datasources = ['foo', 'bar']
[MyDomain1, MyDomain2].each { Class clazz ->
mockDomain(clazz)
grailsApplication.config.dynamic.datasources.each{
clazz.metaClass."get${it.capitalize()}" = {
return delegate
}
clazz.metaClass.'static'."get${it.capitalize()}" = {
return delegate
}
}
}
}
I have controller with following code:
def profile = Profile.findByProfileURL(params.profileURL)
and unit test like this:
#TestMixin(GrailsUnitTestMixin)
#TestFor(ProfileController)
#Mock([User])
class ProfileControllerTests {
def void testIndex() {
mockDomain(User, [[firstname: 'Niko',...]])
controller.params.profileURL = 'niko-klansek'
controller.index()
...
}
}
When I run the test I get following exception in the controller referring to :
No signature of method: sportboard.core.profile.Profile.methodMissing() is applicable for argument types: () values: []
So params value profileURL that I have set in the test is not visible from the controller? How can I set params for controller so it is visible?
Exception is cryptic, but it says that your Profile domain class is not mocked. You should add it to #Mock annotation. Also, #TestMixin can be ommited here and you shouldn't use mockDomain directly in test. Just save this user instance. Altogether it should look like this:
#TestFor(ProfileController)
#Mock([User, Profile])
class ProfileControllerTests {
def void testIndex() {
def user = new User(firstName: 'Niko').save()
controller.params.profileURL = 'niko-klansek'
...
}
}