Unit Testing Authentication - unit-testing

I am fairly new to unit testing. I am building an ASP.NET MVC3 application (although my question seems language agnostic) and am confused about a basic test.
I want to make a unit test that makes sure my "ValidatePassword" function works - It will take in a username and password, then hash the password and see if it matches the hash for a user in the database. If so, it returns true. The problem is that I am using a mock repository, so I will have to add the user to the db before running my test. I can't really create this user in my test setup because I don't know what the encrypted password will be until I actually run it through the function I am testing. Is the answer to run it through the Hash function once, write it down in my test, and then test with that?
Hope this is clear. Thanks!

I prefer to set up my test data where possible through the public interface of my code, rather than giving the test code knowledge of how the code is implemented. So personally I would not use a hardcoded encrypted password in the test code. Let me explain...
Presumably, you have a method to add a new user, which internally will create an new entry in the database with a hashed password. Then the test would look something like this:
AddNewUser("username", "passsword");
bool isValid = ValidateUser("username", "password");
Assert.IsTrue(isValid);
This of course would have to be complimented with invalid user/password tests:
test: ValidUser_InvalidPassword:
AddNewUser("username2", "pwd");
bool isValid = ValidateUser("username2", "wrongPassword");
Assert.IsFalse(isValid);
test: NonExistingUser:
bool isValid = ValidateUser("non_existing_user", "anyPassword");
Assert.IsFalse(isValid);
The argument against this would be that you are testing more than one unit in a single test. But personally I think this is better. Why?
Because the tests are not so brittle - i.e. if you make an internal change to the hashing algorithm the test is there to check that everything still works. You don't have to change the hard coded encrypted password in the test code.
That is one of the main benefits of unit tests: to check that we don't break anything when we refactor. So when we want to change the internal implementation for whatever reason (code cleanliness/performance or security improvements), the tests give us confidence that we have not broken the functionality.
An interesting article discussing the benefits of higher-level tests can be found in this Dr Dobbs article:

Yes, you could have your setup function add the user with an hardcoded encrypted password to the mock repository. When unit-testing, you should use known values so that the behavior of the tested functions can be predicted.

Related

TDD and "honesty" of test

I have a concern with "honesty" of test when doing TDD. TDD is
Write red test
Write just enough code to make it green
Refactor and let the test green
So far so good. Now here is an example of applying the principle above, such kind of example were already met in tutorial & real life :
I want to check that the current user email is displayed on the default page of my webapp.
Write a red test : "example#user.com" is displayed inside default_page.html
Write just enough code to make it green : hardcode "example#user.com" inside default_page.html
Refactor by implementing get_current_user(), some other code in some others layers etc, letting the test green.
I'm "shocked" by step 2. There is something wrong here : the test is green even if nothing is actually working. There a test smell here, it means that maybe at some point someone could break the production code without breaking the test suite.
What I am missing here ?
Your assertion that "nothing is working" is false. The code functions correctly for the case that the email address is example#user.com. And you do not need that final refactoring. Your next failing test might be to make it fail for the case that the user has a different email address.
I would say that what you have is only partially complete. You said:
I want to check that the current user email is displayed on the default page of my webapp.
The test doesn't check the current users email address on the default page, it checks that the fixed email address "example#user.com" is in the page.
To address this you either need to provide more examples (ie have multiple tests with different email addresses) or to randomly generate the email address in the test setup.
So I would say what you have is something like this is pseudo code:
Given current user has email address "example#user.com"
When they visit the default page
The page should contain the email address "example#user.com"
This is the first test you can write in TDD and you can indeed hardcode this to avoid implementing unnecessary stuff. You can now add another test which will force you to implement the correct behavior
Given current user has email address "example2#user.com"
When they visit the default page
The page should contain the email address "example2#user.com"
Now you have to remove the hardcoding as you cannot satisfy both of these tests with a hardcoded solution.So this will force you to get the actual email address from the current user and display this.
Often it makes sense to end up with 3 examples in your tests. These don't need to be 3 separate tests, you can use data driven tests to reuse the same test method with different values. You don't say what test framework you are using, so I can't give a specific example.
This approach is common in TDD and is called triangualtion.
You are correct about
step 2. There is something wrong here
but it's not in the TDD approach. IMHO it's in the test logic. After all this (step 2) validates that the test harness is working correctly. That the new test does not mistakenly pass without requiring any new code, and that the required feature does not already exist.
What I am missing here ?
This step also should tests the test itself, in the negative: it rules out the possibility that the new test always passes, and therefore is worthless. The new test should also fail for the expected reason. It's vital that this step increases the developer's confidence that it is testing the right thing, and passes only in intended cases.

Unit Testing basic Controllers

I have a number of simple controller classes that use Doctrine's entity manager to retrieve data and pass it to a view.
public function indexAction() {
$pages = $this->em->getRepository('Model_Page')->findAll();
$this->view->pages = $pages;
}
What exactly should we be testing here?
I could test routing on the action to ensure that's configured properly
I could potentially test that the appropriate view variables are being set, but this is cumbersome
The findAll() method should probably be in a repository layer which can be tested using mock data, but then this constitutes a different type of test and brings us back to the question of
What should we be testing as part of controller tests?
Controller holds core logic for your application. Though simple "index" controller actions don't have any specific functions, those that verify/actively use data and generate viewmodels have pretty much the most functionality of the system.
For example, consider login form. Whenever the login data is posted, controller should validate login/password and return: 1) to index page whenever logins are good. Show welcome,{user} text. 2) to the login page saying that login is not found in db. 3) to the login page saying that password is not found in db.
These three types of outputs make perfect test cases. You should validate that correct viewmodels/views are being sent back to the client with the appropriate actions.
You shouldn't look at a controller like at something mysterious. It's just another code piece, and it's tested as any other code - any complicated logic that gives business-value to the user should be tested.
Also, I'd recommend using acceptance testing with framework like Cucumber to generate meaningful test cases.
Probably the controller is the hardest thing to test, as it has many dependencies. In theory you should test it in full isolation, but as you already seen - it has no sense.
Probably you should start with functional or acceptance test. It tests your controller action in a whole. I agree with previous answer, that you should try acceptance testing tools. But Cucumber is for Ruby, for PHP you can try using Codeception. It makes tests simple and meaningful.
Also on a Codeception page there is an article on how to test sample controllers.

Django tests reliant on other pages/behaviour

I've started writing some tests for my Django app and I'm unsure how best to structure the code.
Say I have a register page and a page for logged in users only.
My first plan was to have an earlier method perform the register and a later method use that login to test the page:
def test_register_page(self):
//send request to register page and check user has been registered correctly
def test_restricted_page(self):
c = Client();
c.login("someUser","pass");
c.post("/someRestrictedPage/");
//Test response
However this means that now one of my tests rely on the other.
The alternatives I see are calling register in setUp() but this still means that the restricted page test relies on the register page working.
I could try creating a new user manually in setup which I also don't like because this isn't testing a user created by the system.
What is the usual pattern for testing this kind of situation?
You are trying to mix together a lot of different functionalities in one test case. A clean design would be having one test case
for user registration and
one for the view.
Having them depend on each other will introduce a lot of dependencies between them - and - if the test fails the error will be even harder to debug. The success of the registration test should be determined through the correct creation of the user instance (so check necessary attributes etc of the user) and not through being able to login on a certain page. Therefore you will need to set up a "correct" user instance for the view test case. This may seem a bit more complicated than necessary, but it will make future maintainance a lot easier.
What you are trying to do is more something like an integration test, which tests a whole system, but before that you should split up your system in functional units and do unit tests on this units!
The smaller and well-defined the single tests are, the easier will be their maintainance and debugging.

Unit Testing Doctrine ORM Models

I think this is quite a stupid question, but do you test your Models if you already use a ORM framework. I was at it when I realized there isn't much to test? I mainly will just use the models for values? eg. for adding a user
$user = new User();
$user->username = "user1";
$user->password = "password";
$em->persist($user);
$em->flush();
then edit will be similar
$user = /* get user */
$user->email = "new#email.com";
$em->flush();
something like that. The only functionality that I will probably add is register user & change password to handle password salting & verification that username is available.
Maybe another thing is getters & setters? Quite a trivial thing? How will you do that?
public function testCanSetUsername() {
$user->username = "Hello";
$this->assertEquals("Hello", $this->username);
}
Just like that?
You should write story tests that ensure that the code does what the user wants. It sounds like a simple statement, but these are the most important tests. These really tests what the app does rather than how. You'll see that these tests end up covering the code you have in your question.
For example, you may have tests like:
user registers to website
user changes password
etc.
In fact, if you write all these tests and there is code that is not covered by them, you probably don't need that piece of code, and would not have written it in the first place if you had used TDD.

At what level should I unit test?

Let's say in my user model I have a ChangePassword method. Given an already initialised user model, it takes the new password as a parameter and does the database work to make the magic happen. The front end to this is a web form, where the user enters their current password and their desired new password. The controller then checks to see if the user's current password is correct. If so, it invokes the user model's ChangePassword method. If not, it displays an error to the user.
From what I hear you're supposed to unit test the smallest piece of code possible, but doing that in this case completely ignores the check to make sure the user entered the correct current password. So what should I do?
Should I:
A) Unit test only from the controller, effectively testing the model function too?
OR
B) Create 2 different tests; one for the controller and one for the model?
When in doubt, test both. If you only test the controller and the test fails, you don't know whether the issue is in the controller or the model. If you test both, then you know where the problem lies by looking at the model's test result - if it passes, the controller is at fault, if it fails, then the model is at fault.
A)
The test fails. You have a problem in either the model or the controller, or both and spend time searching through the model and controller.
B)
The model and controller tests fail... chances are you have a problem in the model.
Only the controller test fails... chances are better that the problem is not in the model, only in the controller.
Only the model test fails... hard to see this happening, but if it does somehow then you know the problem is in the model, not in the controller.
It's good to test both layers. It'll make finding the problem later that much easier.
There should be multiple tests here:
Verify the correct password was entered.
Validate the new password, e.g. doesn't match existing one, has minimum length, sufficient complexity, tests for errors thrown, etc.
Updating the database to the new password.
Don't forget that the tests can also help act as documentation of the code in a sense so that it becomes clear for what each part of the code is there.
You might want to consider another option: Mock objects. Using these, you can test the controller without the model, which can result in faster test execution and increased test robustness (if the model fails, you know that the controller still works). Now you have two proper unit tests (both testing only a single piece of code each), and you can still add an integration test if required.
Unit testing means to test every unit on its own, so in this case you would need to build two unit tests, one for the frontend and one for the backend.
To test the combination of both an integration test is needed, at least the ITSQB calls it like that.
If you code object oriented you usually build unit tests for every class as that is the smallest independent unit testable.
A) is not a unit test in my opinion since it uses more than one class (or layer). So you should really be unit-testing the model only.