Best Way to Unit Test a Website With Multiple User Types with PHPUnit - unit-testing

I'm starting to learn how to use PHPUnit to test the website I'm working on. The problem I'm running into is that I have five different user types defined and I need to be able to test every class with the different types. I currently have a user class and I would like to pass this to each function but I can't figure out how to pass this or test the different errors that could come back as being correct or not.
Edit: I should have said. I have a user class and I want to pass a different instance of this class to each unit test.

If your various user classes inherit from a parent user class, then I recommend you use the same inheritance structure for your test case classes.
Consider the following sample classes:
class User
{
public function commonFunctionality()
{
return 'Something';
}
public function modifiedFunctionality()
{
return 'One Thing';
}
}
class SpecialUser extends User
{
public function specialFunctionality()
{
return 'Nothing';
}
public function modifiedFunctionality()
{
return 'Another Thing';
}
}
You could do the following with your test case classes:
class Test_User extends PHPUnit_Framework_TestCase
{
public function create()
{
return new User();
}
public function testCommonFunctionality()
{
$user = $this->create();
$this->assertEquals('Something', $user->commonFunctionality);
}
public function testModifiedFunctionality()
{
$user = $this->create();
$this->assertEquals('One Thing', $user->commonFunctionality);
}
}
class Test_SpecialUser extends Test_User
{
public function create() {
return new SpecialUser();
}
public function testSpecialFunctionality()
{
$user = $this->create();
$this->assertEquals('Nothing', $user->commonFunctionality);
}
public function testModifiedFunctionality()
{
$user = $this->create();
$this->assertEquals('Another Thing', $user->commonFunctionality);
}
}
Because each test depends on a create method which you can override, and because the test methods are inherited from the parent test class, all tests for the parent class will be run against the child class, unless you override them to change the expected behavior.
This has worked great in my limited experience.

If you're looking to test the actual UI, you could try using something like Selenium (www.openqa.org). It lets you write the code in PHP (which I'm assuming would work with phpUnit) to drive the browser..
Another approach would be to have a common method that could be called by each test for your different user type. ie, something like 'ValidatePage', which you could then call from TestAdminUser or TestRegularUser and have the method simply perform the same basic validation of what you're expecting..

Just make sure you're not running into an anti-pattern here. Maybe you do too much work in the constructor? Or maybe these should be in fact different classes? Tests often give you clues about design of code. Listen to them.

Related

unit testing src/groovy class files

I'm working on unit tests for a grails application. I've been successful at testing services and domains but stuck trying to test classes in the src/groovy folder. What do I have to do to access the saveCreateAndCall method?
#Artefact("Controller")
#Transactional(readOnly = true)
class BaseController<T> extends RestfulController<T> {
protected def saveCreateAndCall(Object instance, boolean flush = false, Closure c) {
if (instance.save(flush: flush)) {
c.call instance
} else {
errorCreateResponse(instance)
}
}
any advice or feedback would be appreciated, thanks
You can create your BaseController as an abstract class and let it in the controllers folder.
abstract class BaseController<T> {
}
Normally I tend to create an concrete controller to test my base abstraction and use the #TestFor to use the Grails testing features for controllers.
Or you can annotate your test:
#GrailsUnitTestMixin
#Mock(MyDomainClass)
class BaseControllerTest extends Specification {
}

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

Mocking DBSet, EF Model First

As said in the title, I follow Model First method. So my Model classes are Automatically generated. If I want mock the DBContext derived MyModelContainer which contain DBSets of entity classes. Read some where that in order to unit test, you need to change it to IDBSet. Whether its possible to do it especially in a class that gets auto generated when I do "Run Custom Tool" is one concern. But as of now I modified it.
But the real problem is: when I try to Stub MyModelContainer to return a mock generated from IDBSet. Rhino mock is firing an InvalidOperationException: "Invalid call, the last call has been used, or no call has been made(make sure that you are calling a virtual(C#)/Overridable(VB) method."
Here is my unit test code.
MyModelContainer dbMock = MockRepository.GenerateMock<MyModelContainer>();
IDBSet<Models.MyEntity> entityMock = MockRepository.GenerateMock<IDBSet<Models.MyEntity>>()
dbMock.Stub( x=>x.MyEntities ).Return( entityMock );
The last statement is triggering the exception. I tried using the fake implementation of IDBSet<> specified here, But no luck!
I use MVC 4, Rhino Mocks 3.6. Any help will be appreciated.
Update:
After some trials and research, I found a fix. I changed the code to:
MyModelContainer dbMock = MockRepository.GenerateMock<MyModelContainer>();
IDBSet<Models.MyEntity> entityMock = MockRepository.GenerateMock<IDBSet<Models.MyEntity>>()
//dbMock.Stub( x=>x.MyEntities ).Return( entityMock );
dbMock.MyEntities = entityMock;
Now the InvalidOperationException is gone.
The test fails only due to ExpectationViolationException which should be normal.
As for auto generated Model class, it is found out that editing the DbContext's T4 template (.tt extension) will do the trick. Thanks to Alan's Blog
But I want to know why the previous code didn't work. Anyone?
2 reasons are possible here:
MyEntites property of MyModelContainer is not virtual.
In that case Rhino Mock can't stub this property at all. Then dbMock.Stub(x=>x.MyEntities) will fail.
MyEntites property is virtual, but has both public getter and public setter.
Then notation dbMock.Stub(x=>x.MyEntities).Return(entityMock) is not allowed. You can see explanation e.g. here.
In both cases the right fix is exactly what you did: use dbMock.MyEntities = entityMock instead of dbMock.Stub(x=>x.MyEntities).Return(entityMock).
Here is an extension method for Substituting IDbSet (with NSubstitute) to return an IQueryable
public static DbSet<T> FakeDbSet<T>(this IQueryable<T> queryable) where T : class
{
DbSet<T> fakeDbSet = Substitute.For<DbSet<T>, IQueryable<T>>();
((IQueryable<T>)fakeDbSet).Provider.Returns(queryable.Provider);
((IQueryable<T>)fakeDbSet).Expression.Returns(queryable.Expression);
((IQueryable<T>)fakeDbSet).ElementType.Returns(queryable.ElementType);
((IQueryable<T>)fakeDbSet).GetEnumerator().Returns(queryable.GetEnumerator());
fakeDbSet.AsNoTracking().Returns(fakeDbSet);
return fakeDbSet;
}
Then you can now stub the DbContext like this:
var db = NSubstitute.Substitute.For<DataContext>();
var fakeResult = emptyCustomers.FakeDbSet();
db.Customers.Returns(fakeResult);
Here is an extension method for Stubing (with RhinoMocks) IDbSet to return an IQueryable
public static class RhinoExtensions
{
public static IDbSet<T> MockToDbSet<T>(this IQueryable<T> queryable) where T : class
{
IDbSet<T> mockDbSet = MockRepository.GenerateMock<IDbSet<T>>();
mockDbSet.Stub(m => m.Provider).Return(queryable.Provider);
mockDbSet.Stub(m => m.Expression).Return(queryable.Expression);
mockDbSet.Stub(m => m.ElementType).Return(queryable.ElementType);
mockDbSet.Stub(m => m.GetEnumerator()).Return(queryable.GetEnumerator());
return mockDbSet;
}
}
Then you can now stub the DbContext like this:
_db.Stub(p => p.Customers).Return(fakeCustomers.MockToDbSet());

Grails Controller Testing - Problems with dynamic methods

I'm running some old (but valid, i'm told) tests on a legacy application, and notice that many of them arent working. The error message usually is 'No method signature for some dymamic method'
After using mockDomain I managed to solve that problem.
However, I can't figure out how to test controllers that create objects inside.
For example, I created a sample controller (omitted import statements)
package com.tmp
class DummyController2 {
def index = { }
def createObject={
def emp= new Emp(name:'name',description:'description')
if (emp.validate()){
render 'OK'
}
else{
render 'FAIL'
}
}
}
And then the sample controllerTest
package com.tmp
class DummyController2Tests extends ControllerUnitTestCase{
DummyController2 controller
public void setUp(){
super.setUp()
controller = new DummyController2()
}
public DummyController2Tests(){
super(DummyController2Tests)
}
public void tearDown(){
super.tearDown()
}
void testCreateObject(){
assertEquals 'OK',controller.createObject()
}
}
Now when I run this test, I get the
groovy.lang.MissingMethodException: No
signature of method: Emp.validate() is
applicable for argument types: ()
values: []
Is there a workaround on this? Adding mockDomain statements inside the controller seems very intrusive and wrong. Maybe its just that I'm using an old grails (1.2.1)?
Thanks in advance
Your domain class is not mocked. Add to setUp():
mockDomain Emp

Moq custom IIdentity

I created a custom RoleProvider (standard webforms, no mvc) and I would like to test it. The provider itself integrates with a custom implementation of IIdentity (with some added properties).
I have this at the moment:
var user = new Mock<IPrincipal>();
var identity = new Mock<CustomIdentity>();
user.Setup(ctx => ctx.Identity).Returns(identity.Object);
identity.SetupGet(id => id.IsAuthenticated).Returns(true);
identity.SetupGet(id => id.LoginName).Returns("test");
// IsAuthenticated is the implementation of the IIdentity interface and LoginName
However when I run this test in VS2008 then I get the following error message:
Invalid setup on a non-overridable member: id => id.IsAuthenticated
Why is this happening? And most important, what do I need to do to solve it?
Grz, Kris.
You should mock IIdentity (instead of CustomIdentity - only possible if the variables you are mocking are declared in the interface) or declare the used variables as virtual.
To mark as virtual, do this: In your concrete class CustomIdentity, use
public virtual bool isAuthenticated { get; set; }
instead of
public bool isAuthenticated { get; set; }
Moq and other free mocking frameworks doesn't let you mock members and methods of concrete class types, unless they are marked virtual.
Finally, you could create the mock yourself manually. You could inherit CustomIdentity to a test class, which would return the values as you wanted. Something like:
internal class CustomIdentityTestClass : CustomIdentity
{
public new bool isAuthenticated
{
get
{
return true;
}
}
public new string LoginName
{
get
{
return "test";
}
}
}
This class would be only used in testing, as a mock for your CustomIdentity.
--EDIT
Answer to question in comments.
Are you mocking against the interface IIdentity, or mocking against your custom type?
Without having a fuller code snippet to look at, I am guessing that it is complaining that the IsAuthenticated is not marked as virtual in your custom implementation. However, this could only be the case if you were mocking against the concrete type, not the interface.