Apache shiro unit test with checkPermission passes regardless of stubbed permission - unit-testing

Running spring boot 1.5.12 with Shiro starter 1.4.0
Trying to write up some unit tests to test a static class which checks permissions using the checkPermission of the Subject interface.
I'm mocking the shiro subject and stubbing the isPermitted method to return false for specific permission string... but for some reason, it passes when Subject.checkPermission is executed.
Subject subjectUnderTest = mock(Subject.class);
when(subjectUnderTest.isAuthenticated()).thenReturn(true);
when(subjectUnderTest.isPermitted(eq("review:edit:regional"))).thenReturn(false);
setSubject(subjectUnderTest);
subjectUnderTest.checkPermission("review:edit:regional");
I'm fairly new to Mockito but in this case was expecting AuthorizationException to be thrown by checkPermission given the isPermitted stub returning false.
If i change the implementation to use isPermitted, then test runs as expected..but current implementation is using checkPermission...

The method checkPermission() of the Subject interface will not call isPermitted() on itself but on the abstract AuthorizingRealm. The callchain for checkPermission() is like the following:
Subject.checkPermissions()->
DelegatingSubject.checkPermissions()->
Authoriser.checkPermission()->
AuthorizingRealm.checkPermission()->
AuthorizingRealm.isPermitted()
Therefore, mocking isPermitted() on the Subject will have no effect since checkPermission() will never call it on this object. To achieve the expected behavior, you have to mock the method on the AuthorizingRealm or alternatively on the AuthorizingSecurityManager incase calls to your SecurityManager are available under test.

Related

Mocks vs Stubs in PHPUnit

I know stubs verify state and the mocks verify behavior.
How can I make a mock in PHPUnit to verify the behavior of the methods? PHPUnit does not have verification methods (verify()), And I do not know how to make a mock in PHPUnit.
In the documentation, to create a stub is well explained:
// Create a stub for the SomeClass class.
$stub = $this->createMock(SomeClass::class);
// Configure the stub.
$stub
->method('doSomething')
->willReturn('foo');
// Calling $stub->doSomething() will now return 'foo'.
$this->assertEquals('foo', $stub->doSomething());
But in this case, I am verifying status, saying that return an answer.
How would be the example to create a mock and verify behavior?
PHPUnit used to support two ways of creating test doubles out of the box. Next to the legacy PHPUnit mocking framework we could choose prophecy as well.
Prophecy support was removed in PHPUnit 9, but it can be added back by installing phpspec/prophecy-phpunit.
PHPUnit Mocking Framework
The createMock method is used to create three mostly known test doubles. It's how you configure the object makes it a dummy, a stub, or a mock.
You can also create test stubs with the mock builder (getMockBuilder returns the mock builder). It's just another way of doing the same thing that lets you to tweak some additional mock options with a fluent interface (see the documentation for more).
Dummy
Dummy is passed around, but never actually called, or if it's called it responds with a default answer (mostly null). It mainly exists to satisfy a list of arguments.
$dummy = $this->createMock(SomeClass::class);
// SUT - System Under Test
$sut->action($dummy);
Stub
Stubs are used with query like methods - methods that return things, but it's not important if they're actually called.
$stub = $this->createMock(SomeClass::class);
$stub->method('getSomething')
->willReturn('foo');
$sut->action($stub);
Mock
Mocks are used with command like methods - it's important that they're called, and we don't care much about their return value (command methods don't usually return any value).
$mock = $this->createMock(SomeClass::class);
$mock->expects($this->once())
->method('doSomething')
->with('bar');
$sut->action($mock);
Expectations will be verified automatically after your test method finished executing. In the example above, the test will fail if the method doSomething wasn't called on SomeClass, or it was called with arguments different to the ones you configured.
Spy
Not supported.
Prophecy
Prophecy is now supported by PHPUnit out of the box, so you can use it as an alternative to the legacy mocking framework. Again, it's the way you configure the object makes it becomes a specific type of a test double.
Dummy
$dummy = $this->prophesize(SomeClass::class);
$sut->action($dummy->reveal());
Stub
$stub = $this->prophesize(SomeClass::class);
$stub->getSomething()->willReturn('foo');
$sut->action($stub->reveal());
Mock
$mock = $this->prophesize(SomeClass::class);
$mock->doSomething('bar')->shouldBeCalled();
$sut->action($mock->reveal());
Spy
$spy = $this->prophesize(SomeClass::class);
// execute the action on system under test
$sut->action($spy->reveal());
// verify expectations after
$spy->doSomething('bar')->shouldHaveBeenCalled();
Dummies
First, look at dummies. A dummy object is both what I look like if you ask me to remember where I left the car keys... and also the object you get if you add an argument with a type-hint in phpspec to get a test double... then do absolutely nothing with it. So if we get a test double and add no behavior and make no assertions on its methods, it's called a "dummy object".
Oh, and inside of their documentation, you'll see things like $prophecy->reveal(). That's a detail that we don't need to worry about because phpspec takes care of that for us. Score!
Stubs
As soon as you start controlling even one return value of even one method... boom! This object is suddenly known as a stub. From the docs: "a stub is an object double" - all of these things are known as test doubles, or object doubles - that when put in a specific environment, behaves in a specific way. That's a fancy way of saying: as soon as we add one of these willReturn() things, it becomes a stub.
And actually, most of the documentation is spent talking about stubs and the different ways to control exactly how it behaves, including the Argument wildcarding that we saw earlier.
Mocks
If you keep reading down, the next thing you'll find are "mocks". An object becomes a mock when you call shouldBeCalled(). So, if you want to add an assertion that a method is called a certain number of times and you want to put that assertion before the actual code - using shouldBeCalledTimes() or shouldBeCalled() - congratulations! Your object is now known as a mock.
Spies
And finally, at the bottom, we have spies. A spy is the exact same thing as a mock, except it's when you add the expectation after the code - like with shouldHaveBeenCalledTimes().
https://symfonycasts.com/screencast/phpspec/doubles-dummies-mocks-spies

Symfony2: Mocked service is set in the container but not used by the controller (it still uses the original service)

I'm writing the functional tests for a controller.
It uses a class to import some data from third party websites and to do this I wrote a class that I use into Symfony setting it a service.
Now, in my functional tests, I want to substitute this service with a mocked one, set it in the container and use it in my functional tests.
So my code is the following:
// Mock the ImportDataManager and substitute it in the services container
$mockDataImportManager = $this->getMockBuilder('\AppBundle\Manager\DataImportManager')->disableOriginalConstructor()->getMock();
$client->getContainer()->set('shq.manager.DataImport', $mockDataImportManager);
$client->submit($form);
$crawler = $client->followRedirect();
As I know that between each request the client reboots the kernel and I have to set again the mocked class, I set the mock immediately before the calling to $client->submit.
But this approach seems not working for me and the controller still continue to use the real version of the service instead of the mocked one.
How can I use the mocked class to avoid to call the remote website during my functional test?
If I dump the set mocked service, I can see it is correctly set:
dump($client->getContainer()->get('shq.manager.DataImport'));die;
returns
.SetUpControllerTest.php on line 145:
Mock_DataImportManager_d2bab1e7 {#4807
-__phpunit_invocationMocker: null
-__phpunit_originalObject: null
-em: null
-remotes: null
-tokenGenerator: null
-passwordEncoder: null
-userManager: null
}
But it is not used during the $form->submit($form) call and, instead, is used the original service.
UPDATE
Continuing searching for a solution, I landed on this GitHub page from the Symfony project, where a user asks for a solution to my same problem.
The second call doesn't use the mocked/substituted version of his class, but, instead, the original one.
Is this the correct behavior? So, is it true that I cannot modify the service container on a second call to the client?
Yet, I don't understand why the service is not substituted in the container and I haven't a real solution to that problem.
Anyway I found some sort of workaround, in reality more correct as solution (also if it remains unclear why the service is not substituted and this is a curiosity I'd like to solve - maybe because the $client->submit() method uses the POST method?).
My workaround is a simple test double.
I create a new class in AppBundle/Tests/TestDouble and called it DataImportManagerTestDouble.php.
It contains the unique method used by the controller:
namespace AppBundle\Tests\TestDouble;
use AppBundle\Entity\User;
class DataImportManagerTestDouble
{
public function importData(User $user)
{
return true;
}
}
Then, I instantiate it in the config_test.yml (app/config/config_test.yml) file in the following way:
services:
shq.manager.DataImport:
class: AppBundle\Tests\TestDouble\DataImportManagerTestDouble
This way, during the tests, and only during the tests, the class loaded as service is the TestDouble and not the original one.
So the test pass and I'm (relatively) happy. For the moment, at least.

Unit test class with stubs. Should I configure stubs to return ALWAYS correct values?

this is one of my questions about unit testing.
I'm reading The Art Of Unit Testing and at chapter 3 the author shows how to remove dependency between one or more classes. That seems clear to me. What's not absolutely clear is the following point.
When I configure a test method with a stub, I configure it to return a specific value. Then I call the tested method exposed by the tested class. This method executes some logic and uses the return value of the stub. The problem is: if the stub is configured to return the wrong value my test will probably fail.
So the question is: when I use stubs, should I ALWAYS configure them to return the expected value? In my opininon this should be the correct way to test as if the stub return always the expected value I'm sure to test only the logic inside the tested method.
What do you think about this? Is there some case in which has some kind of sense to oblige the stub to return uncorrect values?
Thanks a lot,
Marco
You are testing how the sut (system under test) works under several conditions:
the good path = configuring stubs to return good values and test that the sut behaves accordingly
the sad path(s) = configure stubs with wrong values and verify that the sut can handle such cases (e.g. you can test that it throws an Exception using the ExpectedException attribute if you're using nUnit)
You could configure the stub method to return value depending on the test setup in some scenarios. In others to return default value, which should be valid.

call to .save() on domain class fails with no signature of method .save() in unit test

I'm using Grails 1.3.2 in NetBeans. I have a very simple unit test that fails with this error:
No signature of method: com.maxrecall.maxrequire.domain.Release.save()
is applicable for argument types: (java.util.LinkedHashMap) values:
[[flush:true]] Possible solutions: wait(), any(), wait(long),
isCase(java.lang.Object), use([Ljava.lang.Object;), sleep(long)
The relevant code is:
Release rel = new Release()
...
rel.save(flush:true)
The same code works in BootStrap.groovy. I have tried various variations on .save() (.Save with and without parameters.
It's a unit test, so there's no Spring, Hibernate, etc. You have to mock the behavior you want.
If you're testing persistence, you need to convert the test to an integration test, since testing persistence with unit tests just tests the mocking framework. An integration test uses at a minimum an in-memory database, although you can easily switch that for a test instance of MySQL/Oracle/etc.
If you're testing controllers or other users of your domain classes and just want them to work so you can focus on testing the current class, use mockDomain and the other mock methods available in the Grails unit test base classes.
This is all described in detail in the documentation - see http://grails.org/doc/latest/guide/9.%20Testing.html
There's no grails magic around in a unit test (it's there in an integration test). See "Understanding mockDomain()" here or read the Grails docs.

Strategies to mock a webservice

I'm implementing a client consuming a webservice. I want to reduce dependencies and decided to mock the webservice.
I use mockito, it has the advantage vs. EasyMock to be able to mock classes, not just interfaces. But that's not the point.
In my test, I've got this code:
// Mock the required objects
Document mDocument = mock(Document.class);
Element mRootElement = mock(Element.class);
Element mGeonameElement = mock(Element.class);
Element mLatElement = mock(Element.class);
Element mLonElement = mock(Element.class);
// record their behavior
when(mDocument.getRootElement()).thenReturn(mRootElement);
when(mRootElement.getChild("geoname")).thenReturn(mGeonameElement);
when(mGeonameElement.getChild("lat")).thenReturn(mLatElement);
when(mGeonameElement.getChild("lon")).thenReturn(mLonElement);
// A_LOCATION_BEAN is a simple pojo for lat & lon, don't care about it!
when(mLatElement.getText()).thenReturn(
Float.toString(A_LOCATION_BEAN.getLat()));
when(mLonElement.getText()).thenReturn(
Float.toString(A_LOCATION_BEAN.getLon()));
// let it work!
GeoLocationFetcher geoLocationFetcher = GeoLocationFetcher
.getInstance();
LocationBean locationBean = geoLocationFetcher
.extractGeoLocationFromXml(mDocument);
// verify their behavior
verify(mDocument).getRootElement();
verify(mRootElement).getChild("geoname");
verify(mGeonameElement).getChild("lat");
verify(mGeonameElement).getChild("lon");
verify(mLatElement).getText();
verify(mLonElement).getText();
assertEquals(A_LOCATION_BEAN, locationBean);
What my code shows is that I "micro-test" the consuming object. It's like I would implement my productive code in my test. An example for the result xml is London on GeoNames.
In my opinion, it's far too granular.
But how can I mock a webservice without giving everystep? Should I let the mock object just return a XML file?
It's not about the code, but the approach.
I'm using JUnit 4.x and Mockito 1.7
I think the real problem here is that you have a singleton that calls and creates the web service so it is difficult to insert a mock one.
You may have to add (possibly package level) access to the singleton class. For example if the constructor looks something like
private GeoLocationFactory(WebService service) {
...
}
you can make the constructor package level and just create one with a mocked web service.
Alternatively you can set the webservice by adding a setter method, although I don't like mutable Singletons. Also in that case you have to remember to unset the webservice afterwards.
If the webservice is created in a method you might have to make the GeoLocationFactory extensible to substitute the mock service.
You may also look into remove the singleton itself. There are articles online and probably here on how to do that.
you really want to be mocking the results returned from the webservice to the code that will be using the result. In your example code above you seem to be mocking mDocument but you really want to pass in an instance of mDocument that has been returned from a mocked instance of your webservice and assert that the locationBean returned from the geoLocationFetcher matches the value of A_LOCATION_BEAN.
The easiest option would be to mock the WebService client,
when(geoLocationFetcher.extractGeoLocationFromXml(anyString()))
.thenReturn("<location/>");
You can modify the code to read the response xml from the file system.
Sample code can be found here: Mocking .NET WebServices with Mockito