Q:

Apex Unit Test giving different results for SOQL query when using runAs

I'm writing a unit test to test code that is being used both by regular salesforce users and the site guest user. In order to do so, I'm using the System.runAs method. I was getting weird results, and narrowed it down to the fact that when using runAs guest user, I get different query results.
Some setup code:
    Contact con2 = new Contact(FirstName = 'Test1', LastName = 'Test2',
                                MobilePhone='222-222-2222', Email='test1@test.com');
    insert con2;

   Contact con_a = [SELECT Id from contact where email = 'test1@test.com' LIMIT 1];

This gives me a result.
However, when I do the following:
    User signUpFormUser = [SELECT Id, FirstName FROM User WHERE FirstName = 'SignUpForm' LIMIT 1];
    System.runAs(signUpFormUser){
        Contact con_s = [SELECT Id from contact where email = 'test1@test.com' LIMIT 1];

    }

I get a List has no rows for assignment to SObject exception.
Why does this happen? How can I fix it? 

A:

When you use System.runAs(), you're establishing a different execution context, one where the record visibility for a specific user is implicitly enforced just like with Apex code that is running under the with sharing regime. In that new execution context you'll have the appropriate sharing rules and visibility for its running user applied to query results.
Presumably, in your organization, the Org-Wide Default for the Contact object is set to Private. Hence, con2 is owned by the original test-running user, and when the test code switches into the context of signUpFormUser, your test methods and with sharing Apex cannot see that Contact.
When you directly assign an Sobject to the result of a query (rather than a List<Sobject>) and the query returns no rows, you'll always get an exception. To avoid this, assign to a List and check the count returned.

