Title: Don't test only with superusers
Slug: don-t-test-only-with-superusers
Date: 2011-07-17 18:32:54
Author: Kartones
Lang: en
Tags: Development, Testing, Patterns & Practices
Description: The importance of testing software with normal user accounts, not just admin accounts, to avoid potential issues and ensure functionality for all users.

 <p>One common pitfall that keeps poping up in my life is doing tests always with superusers/admins/roots, and then having problems with normal accounts.</p> <p>This is utterly wrong, because <strong>admins are the smallest  segment of your users</strong>, and thus should be the last ones to take into account for tests.</p> <p> </p> <p>A typical scenario is disallowing doing stuff, accessing content, etc.:</p> <p>In a screen of a "delete content" control you might want to optimize the code by not doing anything if you are not supposed to view it; The typical "<font face="Courier New">if (user.canDelete(Item) { //renderdelete button } else { //do nothing }</font>". </p> <p><br>But what happens if you forget to start the rendering code/markup with a "<font face="Courier New">if (user.canDelete(Item) { ...</font>" ? </p> <p>Either everybody will see the  delete button, or something will break further in (hopefully, as allowing everyone to delete content is catastrophic).</p> <p>It is very important to either have a mechanism/architecture to add this checks automatically, or to double and triple check they are present, and in the correct spot.</p> <p> </p> <p>Another example of bad practices:</p> <p>Imagine you are building an item deletion feature: A user can delete something if he was the creator, or if he is an admin. <br>First approach could be to implement a <font face="Courier New">user.CanDelete(item)</font> that checks <font face="Courier New">user.IsOwner(item) || user.IsAdmin(item)</font> and this will work until we add another category of user (for example, moderator). Now we need a <font face="Courier New">user.IsModerator(content)</font> because is not an admin.</p> <p>I've seen people do similar approaches as:</p> <p><font face="Courier New"><font color="blue">public bool </font><font color="black">CanDelete(</font><font color="blue">object </font></font><font face="Courier New"><font color="black">Item)<br>{<br>    </font><font color="blue">bool </font><font color="black">canDelete </font></font><font face="Courier New"><font color="blue">= true;<br>    if </font><font color="black">(!</font><font color="blue">this</font><font color="black">.IsOwner(Item) &amp;&amp; !</font><font color="blue">this</font></font><font color="black" face="Courier New">.IsAdmin(Item))<br>    {<br>        canDelete </font><font color="blue" face="Courier New">= false;<br>    </font><font face="Courier New"><font color="black">}<br>    </font><font color="blue">return </font><font color="black">canDelete</font></font><font face="Courier New"><font color="blue">;<br></font>}</font></p> <p>A basic refactor to just one line would avoid the false positive problem, but wouldn't fix the false negative that might arise. Plus it places all users in two flags: owners/no-owners and admins/no-admins. Adding a moderator would need a first rewrite. Adding special users would need more. Adding visitors (maybe read-only access to every part of the site/application) might need even more checks.</p> <p>The solution is quite easy: Build a role +permissions system, doing more "meta-data" coding (<font face="Courier New">user.IsInRole(xxx)</font>, <font face="Courier New">role.CanDelete(xxx)</font>, etcetera). It is way more better to have a role-based security, with  <font face="Courier New">HasPermissions(action)</font> methods, or a permissions manager and then send both the action and subject to it.</p> <p>Any solution that tries to simplify the logic as much as possible, encapsulating the logic and adding reusability in the process; providing as less entry points to security checks as possible, so that you know you have to only check and modify security logic in one or two places.</p> <p>It is critical to test this delicate conditionals, so if they are either by convention in a method or all in a separate class make sure to see the logic and try to break it in the tests that fully cover all cases.</p> <p> </p> <p>Also, by testing with superusers we might get side-effects:</p> <ul> <li>Longer response times: Because as an admin you might get additional data, a slower perception, longer loading times...  <li>Specific admin errors: Mere verbosing for admins can create new errors or generate Out of Memory exceptions by the extra amount or data or broken special features. <li>Wrong privacy tests: Being an admin you will probably see everything, be able to comment everywhere, etc. Use a normal account to see what the user exactly sees.  <li>Beta or different menus/features: If your beta features are activate for admins (without a finer control) you can forget that what you see is the new version but that there is already an existing one, maybe with different algorithms, security or behaviour. <li>Relaxed or removed limitations: Rate limiting removed? Premium content? Those should be different tests, not the normal behaviour.</li></ul> <p> </p> <p>Just think what is better: To have your website broken for the two administrators, or for thousands of users and visitors? First make sure your site works for the common scenario and majority of users, then go with the non-standard cases or the rest of the roles.</p>
