Testing With config.require_tenant = true
I am getting
In
ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet
on tests related to a model I have called Entity
.In
acts_as_tenant.rb
I haveActsAsTenant.configure do |config| config.require_tenant = true end
In
entities_controller.rb
I have: acts_as_tenant :account
How do I set up my tests to have an account created, set as the current_tenant
, and then assigned to new entities created in my Entity fixture?This is my Entity fixture (pretty basic):
one: name: MyString description: MyString
Any help on this would be much appreciated! Thanks!
Hey Jason!
The multitenancy test file has some examples you can use for the various ways of selecting a tenant. https://gitlab.com/gorails/jumpstart-pro/-/blob/master/test/integration/multitenancy_test.rb
Depending what you have enabled, you can use whatever you need. The `switch_account` method uses the session cookie, which is always enabled so you can use that by default if you need.
You'll want to make sure your Entity fixture is associated with a tenant so that it is in the query results for the current tenant.
The multitenancy test file has some examples you can use for the various ways of selecting a tenant. https://gitlab.com/gorails/jumpstart-pro/-/blob/master/test/integration/multitenancy_test.rb
Depending what you have enabled, you can use whatever you need. The `switch_account` method uses the session cookie, which is always enabled so you can use that by default if you need.
You'll want to make sure your Entity fixture is associated with a tenant so that it is in the query results for the current tenant.
While I've had some luck with this:
class OwnershipTest < ActiveSupport::TestCase setup do ActsAsTenant.current_tenant = accounts(:company) end ... end
I cannot seem to replicate this in an integration test:
class EntitiesControllerTest < ActionDispatch::IntegrationTest setup do @user = users(:one) sign_in @user @account = accounts(:company) ActsAsTenant.current_tenant = @account @entity = entities(:main) end ... test "should show entity" do get entity_url(@entity) assert_response :success end
test "should create entity" do assert_difference('Entity.count') do post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } } end assert_redirected_to entity_url(Entity.last) end ... end
When I run the EntitiesControllerTest I get the following errors on the two tests shown above (amongst others):
Error:EntitiesControllerTest#test_should_create_entity: ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>' test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>'
EntitiesControllerTest#test_should_show_entity: ActiveRecord::RecordNotFound: Couldn't find Entity with 'id'=1059638630 [WHERE "entities"."account_id" = $1] app/controllers/entities_controller.rb:60:in `set_entity' lib/jumpstart/lib/jumpstart/account_middleware.rb:30:in `call' test/controllers/entities_controller_test.rb:33:in `block in <class:EntitiesControllerTest>'
Using byebug, I inspected the value of @entity and @account and found that:
@entity = #<Entity id: 1059638630, name: "main", description: "main", created_at: "2020-05-14 01:58:26", updated_at: "2020-05-14 01:58:26", account_id: 264178000> @account = <Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-05-14 01:58:26", updated_at: "2020-05-14 01:58:26", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil>
I'm not sure, but it seems like it is trying to find an entity where entity.account_id = 1, but the account_id is actually 264178000. That said, I don't know why it is looking for any entity where entity.account_id is 1. Any help getting these tests to pass would be much appreciated! I'm trying to do TDD, but I can't even get the basic tests to pass and am starting to pull my hair out! Thanks!
I would recommend using the same mechanisms I have in the test file example like
You also may want to work backwards from the entity to make sure that you're logging in a user that's actually connected to the account. It's easy for fixtures to change and the user ends up not having access to the account, breaking your tests.
switch_account(@account)
. That way you're going through the same setup that Rails uses to set the current tenant and you don't have to do that directly in your tests.You also may want to work backwards from the entity to make sure that you're logging in a user that's actually connected to the account. It's easy for fixtures to change and the user ends up not having access to the account, breaking your tests.
class EntitiesControllerTest < ActionDispatch::IntegrationTest setup do @entity = entities(:main) @account = @entity.account @user = @account.users.first sign_in @user switch_account(@account) end ... test "should show entity" do get entity_url(@entity) assert_response :success end end
This would more reliably set the account and users since it's using the associations. And then by calling switch_account, you'll let Jumpstart / Rails set the current tenant as it expects it to be set.
Without that, Rails may not know if you're signed into an account or which one.
Thanks Chris! So this worked for most of the integration tests (including the show test above), but not for the create or destroy tests. It seems like it may be an issue with assert_difference being used in both of these, but I'm not sure.
test "should create entity" do assert_difference('Entity.count') do post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } } end assert_redirected_to entity_url(Entity.last) end test "should destroy entity" do assert_difference('Entity.count', -1) do delete entity_url(@entity) end assert_redirected_to entities_url end
I'm still getting the no tenant error on both:
EntitiesControllerTest#test_should_create_entity:ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>' test/controllers/entities_controller_test.rb:24:in `block in <class:EntitiesControllerTest>' EntitiesControllerTest#test_should_destroy_entity:ActsAsTenant::Errors::NoTenantSet: ActsAsTenant::Errors::NoTenantSet test/controllers/entities_controller_test.rb:47:in `block in <class:EntitiesControllerTest>' test/controllers/entities_controller_test.rb:47:in `block in <class:EntitiesControllerTest>'
Thanks again for your help with this!
P.S. Since these use devise's integration test helpers, they don't seem to work in a model test, which inherits from ActiveSupport::TestCase. In that test I used ActsAsTenant.current_tenant = accounts(:company) and it seems to work. Is that the recommended method?
switch_account actually makes a HTTP request, so it's only available in controller tests.
Setting the current tenant would be what you want for model tests. It would make sense to add a helper so we can set the Current.account and current_tenant together for model tests. I'll have to look into adding that.
Setting the current tenant would be what you want for model tests. It would make sense to add a helper so we can set the Current.account and current_tenant together for model tests. I'll have to look into adding that.
Actually, I'm wrong. I am using path. I think my problem is that after using `switch_account @account` in my test, the CurrentHelper::current_account is still pointing to the previously logged in user. So my before_action ensuring that certain actions are only accessible to the non-personal account is redirecting me in my tests when they shouldn't be.
It looks like my problem is actually the path helpers. They aren't putting the account_id at the beginning of the path after I call switch_account. It looks like I have to provide the `script_name: "/#{@account.id}"` in my controller tests. Is there a reason why this isn't required when using the path helpers in erb?
One thing you have to keep in mind is that the tests are not running a real browser, so the script_name won't necessarily persist the same way that it does in the browser.
I wonder if this is related to what you're seeing in the path helpers for tests.
https://github.com/rails/rails/issues/26173
For setting accounts manually in tests, you can do this which will replicate what the SetCurrentRequestDetails does.
Current.account = account ActsAsTenant.current_tenant = account
If you've got time, could you guys create a private repo that reproduces the problem with a test that I can look at?
Here's another example of this issue cropping up (this time in a system test):
class EntitiesTest < ApplicationSystemTestCase setup do @entity = entities(:main) @account = @entity.account @user = @account.users.first login_as @user, scope: :user end test "updating an Entity" do ActsAsTenant.current_tenant = @account ### I added this line because putting out the current_tenant was returning nil before using this puts ActsAsTenant.current_tenant.inspect puts @account.inspect puts @entity.inspect visit entity_url(@entity) click_on "Edit", match: :first fill_in "Description", with: @entity.description fill_in "Name", with: @entity.name click_on "Update Entity" assert_text "Entity was successfully updated" assert_text "Entities" end end
The output in the log is:
#<Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil> #<Account id: 264178000, name: "Company", owner_id: 980190962, personal: false, created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", processor: nil, processor_id: nil, trial_ends_at: nil, card_type: nil, card_last4: nil, card_exp_month: nil, card_exp_year: nil, extra_billing_info: nil, domain: "company.com", subdomain: "company", plan: nil, quantity: nil, card_token: nil> #<Entity id: 1059638630, name: "main", description: "main", created_at: "2020-06-24 17:36:30", updated_at: "2020-06-24 17:36:30", account_id: 264178000, tag_list: nil>
The entity's account_id matches the ID of @account and matches the ID of the account returned by ActsAsTenant.current_tenant, but I'm still getting this error:
ActiveRecord::RecordNotFound: Couldn't find Entity with 'id'=1059638630 [WHERE "entities"."account_id" = $1]
Actually, it's being cleared out after the post request in your test since it's a thread local variable. Seems like a bug in ActsAsTenant.
This works, for example:
This works, for example:
test "should create entity" do ActsAsTenant.current_tenant = @account assert_difference('Entity.count') do post entities_url, params: { entity: { description: @entity.description, name: @entity.name, account: @account } } ActsAsTenant.current_tenant = @account end assert_redirected_to entity_url(Entity.last) end
Another situation where relying on gems is a bad idea. We may have to rewrite ActsAsTenant and build our own that works better.
System tests run in a real browser, so you'll want to run the same steps the user would have to in the browser. Have them switch to the correct account, then execute your test.
Basically:
1. click_on account in navbar
2. run test as normal
You can't use the switch_account helper there because you're running a real browser and you need to simulate real user interactions. They would click on the account in the navbar to switch, then fill out forms and click buttons.
Basically:
1. click_on account in navbar
2. run test as normal
You can't use the switch_account helper there because you're running a real browser and you need to simulate real user interactions. They would click on the account in the navbar to switch, then fill out forms and click buttons.
visit entities_url #or some index page on your app where the nav_bar is visible find_by_id('navbar-dropdown-toggle').click #the css id of the div that contains the avatar and toggles the dropdown menu click_link "#{@account.name}", {wait: true} assert_text "Dashboard"
Without the assert_text there it was not waiting for the page to load and was running the tests before the account change was processed.
I solved this tenancy issue for the tests by adding these two helpers to the `test_helper.rb`:
def assert_difference(expression, *args, &block) wrapped_block = proc do ActsAsTenant.without_tenant do yield end end super(expression, *args, &wrapped_block) end def assert_difference_with_tenant(account, expression, *args, &block) ActsAsTenant.current_tenant = account wrapped_block = proc do result = yield ActsAsTenant.current_tenant = account result end assert_difference(expression, *args, &wrapped_block) ActsAsTenant.current_tenant = account end
Just posting this here in case anyone else still runs into this issue.
Notifications
You’re not receiving notifications from this thread.