How to test the model with Minitest & no database? - ruby-on-rails-4

I am using rails 4.2.1 & ruby 2.2.1 in my appliation. So minitest is automatically added which is of version 5.1. There is no database in my application.
With database I am able to test the model. How do I test the model without database?
I have created a user model:
class User
include ActiveModel::Model
end
users.yml:
one:
firstName: Avi
email: a#a.a
user_test.rb:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
test "the truth" do
user = users(:one)
assert true
end
end
Here I am getting the error: Undefined methods users. I am getting proper data if daabase exists.
I even tried adding include ActiveModel::Lint::Tests still getting the same error.
Can anyone help me on this?
Thanks

The class ActiveSupport::TestCase expects that a database connection is active. You probably want to switch that to Minitest::Test, but that means that you can't use the users fixture method to retrieve a record from the database.
require 'test_helper'
class UserTest < Minitest::Test
def test_sanity
user = User.new
user.first_name = "Avi"
user.email = "a#a.a"
assert user
end
end

Related

Location for user_parameter_sanitizer.rb

Recommendation from GitHub devise gem:
If you have multiple Devise models, you may want to set up a
different parameter sanitizer per model. In this case, we recommend
inheriting from Devise::ParameterSanitizer and adding your own
logic:
class User::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
permit(:sign_up, keys: [:username, :email])
end
end
And then configure your controllers to use it:
class ApplicationController < ActionController::Base
protected
def devise_parameter_sanitizer
if resource_class == User
User::ParameterSanitizer.new(User, :user, params)
else
super # Use the default one
end
end
end"
Where should I create the file user_parameter_sanitizer.rb to host the code?
The info given by the devise GitHub page is excellent, but I am new to Rails and coding in general.

Is there a better way of overriding devise controller in rails

I have to populate a field in my db when a new user signs-up.But this field is not to be filled by the user, instead will be populated by the application in the db. Here is what i tried:
Added the extra field(investorId) in the migration file.
Overridden the devise controller:
def create
super
if #user.save
#user.investorId = #user.id + X---> some number
#user.save
end
end
Though it is working fine, but I want to know if there are better ways of doing it since I am doing it for the first time.
Thanks,
Sachin
If you need to generate a value before you create a record, you can use the before_create or after_create callbacks on the model.
User < ActiveRecord::Base
after_create :generate_investor_id
private
def generate_investor_id
reload
investor_id = "#{self.id}something"
update_attribute(:investor_id, investor_id)
end
end
Don't override your devise controller, there is no benefit of doing this. Simply put the below logic in your after_create callback -
User < ActiveRecord::Base
after_create :create_investor_id
private
def create_investor_id
self.update_column(:investor_id, id+some_number)
end
end

How make Current user as admin by using update query in Rails 4

I need to do current user as admin. So I am trying to update my users table. In my user table there is a field called admin which has boolean datatype.
if current_user.admin != true
current_user.update_attributes(:admin => 'true')
end
But this is not reflecting in my table. So what should I do here?
Try this
if current_user.admin != true
current_user.admin = true #if admin is boolean
current_user.save
end
This should work.
make it more "generic"
class User
def grant_admin!
update_attribute(:admin, true)
# you dont need to check if he is alread an admin since rails won't
# write to DB unless any value changed
end
end
# in controller
#...
current_user.grant_admin!
Do yourself a favor and have a look at https://github.com/ryanb/cancan and its railscasts

Django tests work using SQLite but not MySQL

The tests that I have written for my Django application have been working perfectly during initial development where I have been using SQLite. Now that I am getting ready to deploy I have setup a MySQL server (as that is what I'll be deploying to) but now some of my tests are failing.
Lastly the tests that are failing don't fail when I manually test the functionality.
What could be going on?
I'm not doing anything unusual, all of the views do some database shenanigans and return a response. There isn't anything timing related (no threading or anything).
The tests all inherit from django.test.TestCase and I'm not using any fixtures.
Here is an example of a test that fails.
class BaseTest(TestCase):
def setUp(self):
super(BaseTest, self).setUp()
self.userCreds = dict(username='test', password='a')
# Create an admin user
admin = models.User.objects.create_superuser(
email='', username='admin', password='a')
# Create a user and grant them a licence
user = models.User.objects.create_user(
email='some#address.com', first_name="Mister",
last_name="Testy", **self.userCreds)
profile = models.getProfileFor(user)
node = profile.createNode(
'12345', 'acomputer', 'auser',
'user#email.com', '0456 987 123')
self.node = node
class TestClientUIViews(BaseTest):
def test_toggleActive(self):
url = reverse('toggleActive') + '?nodeId=%s' % self.node.nodeId
self.assertFalse(self.node.active)
# This should fail because only authenticated users can toggle a node active
resp = self.client.get(url)
self.assertEqual(resp.status_code, 403)
self.assertFalse(self.node.active)
# Login and make sure visiting the url toggles the active state
self.client.login(**self.userCreds)
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertTrue(self.node.active)
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
self.assertFalse(self.node.active)
And here is what the model looks like:
class Node(models.Model):
#property
def active(self):
'''
Activation state gets explictly tracked in its own table but is
exposed as a property for the sake of convenience
'''
activations = NodeActivation.objects \
.filter(node=self) \
.order_by('-datetime')
try:
return activations[0].active
except IndexError:
return False
#active.setter
def active(self, state):
if self.active != state:
NodeActivation.objects.create(node=self, active=state)
class NodeActivation(models.Model):
node = models.ForeignKey("Node")
datetime = models.DateTimeField(default=datetimeM.datetime.now)
active = models.BooleanField(default=False)
My local MySQL is 5.5.19 (so its using InnoDB) but I get the same failures on the deployment server which is using 5.1.56. The tests fail regardless of the storage engine.
And as I mentioned at the beginning, if I switch back to use a SQLite database, all the tests go back to passing.
With more of the actual code now revealed I'll provide the following hypothesis as to why this test is failing.
The NodeActivation model is incorrect. The datetime field should be:
datetime = models.DateTimeField(auto_now=True)
Using datetime.datetime.now() in a model definition will only be evaluated once.
Every time the setter creates a new NodeActivation record, the record will be created with the same date/time. ie The date/time that the NodeActivation model was first evaluated.
Your getter only ever returns a single result. But since both activation records have the same date/time, the ordering may be dependent on the database back end. There will be two NodeActivation records in your database at the end of the test, which one is returned is indeterminate.
By changing the active property on the Node model class to this:
#property
def active(self):
'''
Activation state gets explictly tracked in its own table but is
exposed as a property for the sake of convenience
'''
activations = NodeActivation.objects \
.filter(node=self) \
.order_by('-id')
try:
return activations[0].active
except IndexError:
return False
the problem goes away.
Note the change to the order_by call.
The records were getting created so quickly that ordering by datetime wasn't deterministic, hence the erratic behaviour. And I guess SQLite is just slower than MySQL which is why it wasn't a problem when using it as the backing database.
NOTE: Thanks to Austin Phillips for the tip (check out comments in his answer)
I had a similar problem (not sure if its the same) going from SQLite to PostgreSQL using a setup method like you have for your initial database objects.
Example::
def setUp(self):
user = User.objects.create_user(username='john', password='abbeyRd', email='john#thebeatles.com')
user.save()
I found that this would work fine on SQLite, but on PostgreSQL I was getting database connection failures (it would still build the database like you said, but couldn't run the tests without a connection error).
Turns out, the fix is to run your setup method as follows:
#classmethod
def setUpTestData(cls):
user = User.objects.create_user(username='john', password='abbeyRd', email='john#thebeatles.com')
user.save()
I don't remember the exact reason why this works and the other one didn't, but it had something to do with the connection to the database trying to reconnect every test if you use setUp(self) and if you use setUpTestData(cls) it only connects when the class is instantiated, but I might not have the details perfect. All I really know is it works!
Hope that helps, not sure if it addresses your exact issue, but it took me a good few hours to figure out, so I hope it helps you and maybe others in the future!

django custom form validation with foreign keys

I have the following models:
class Computer(models.Model):
...
class Demo(models.Model):
computers = models.ManyToManyField(Computer)
...
class Scenario(models.Model):
demo = models.ForeignKey(Demo)
...
class Setting(models.Model):
scenario = models.ForeignKey(Scenario)
computer = models.ForeignKey(Computer)
Basically a Demo uses multiple computers. A demo also has multiple scenarios. Each scenario has some settings and each setting configures a computer.
My problem is while using the django Admin site to add a scenario, after the user selects a demo in the drop down list and configures the settings for some computers, I need to validate that the computers in the settings are actually in the demo.
I've poured through the django documentation, online sites, and tried everything I can think of and still can't get this work.
I can't use the custom form validation because although I could get the 'demo' object from the cleaned_data in the scenario form, I can't seem to get access to the settings that get submitted with the form. If I do model level validation by overriding 'clean', that only works when I change a scenario not when I add a new one because the computer_set is empty for new ones.
Any help is greatly appreciated.
You could just add a custom form to your SettingInline (I'm assuming from your post that Setting is an inline for Scenario).
You mention you can't use form validation but I don't see a reason why you'd need access to every other setting. If you want access to the other settings (say validation that involves all submitted settings) I'd override the formset itself.
class SettingForm(forms.ModelForm):
class Meta:
model = Setting
def clean_computer(self):
computer = self.cleaned_data.get('computer')
if not self.instance.scenario.demo.computers.filter(computer=computer).count():
raise forms.ValidationError("Computer not in demo")
return computer
class SettingInline(admin.TabularInline):
model = Setting
form = SettingForm