OK, probably a bad example. Here is an actual example. I would be wanting to unit test the total() method from a shopping cart class to see if the value is what I think it should be, which uses the getAll() (cart items), subTotal() and shipping() methods. Is total() something you could test using PHPUnit ?
class Cart extends ObjectModel
{
protected $uniqueID = 10;
public function getAll()
{
return $this->execute("SELECT *, cart.id AS cart_id FROM cart LEFT JOIN products ON products.id = cart.product_id WHERE cart.unique_id = ? AND cart.quantity > '0' AND cart.deleted_at IS NULL ", [$this->uniqueID] );
}
public function subTotal()
{
$subTotal = 0;
foreach($this->getAll() as $row){
$subTotal += ( $row->quantity * $row->cart_price );
}
return $subTotal;
}
public function total()
{
return $this->subTotal() + $this->shipping();
}
public function shipping()
{
if(isset($_SESSION[SALT.'shipping']) && $_SESSION[SALT.'shipping'] != ''){
return $_SESSION[SALT.'shipping'];
}
/* OR RETURN DEFAULT IF SHIPPING IS NOT SET */
return 0;
}
}
The execute method is as follows, there is no constructor method for the ObjectModel class
public function execute($query, array $array) {
$query = Db::conn()->prepare($query);
$query->execute($array);
return $query->fetchAll(PDO::FETCH_OBJ);
}
Db::conn() is just the PDO database handle.
The class is not too well designed for unit-testing because it is tightly coupled to the database. In a unit-test, you want to test the subject-under-test in isolation of external components (here: the database). Usually, you mock or stub the external components with a test double.
Because the DB instance is hardcoded in the execute method of the ObjectModel, you cannot easily replace it with a test double. You could turn the test into an integration test and change the DB connection to a test database which you then populate with expected values in the tests setup method.
If you don't want to use the database at all, you can self-shunt the subject-under-test. This means, you use a test double of the subject-under-test itself and make execute return the expected values for your test case.
Related
I have a Grails service that creates a criteria query with optional parameters like this:
List<Car> search(String make = "%", String model = "%", Integer year = null) {
def c = Car.createCriteria()
return c.list() {
if(make) {
like("make", make)
}
if(model) {
like("model", model)
}
if(year) {
eq("year", year)
}
}
}
(Also, is this the idiomatic way to do this in grails? I'm quite new to the framework and I'm trying to find the right way to do things)
I'd like to test that the proper criteria filters are set according to the values of the parameters of the search method but I'm having no success.
I tried some variations of this:
#TestFor(CarService)
#Mock(Car)
class CarServiceSpec extends Specification {
def car = Mock(Car)
void "empty filters"() {
when: service.search()
then:
with(car.createCriteria()) {
0 * like(*_)
0 * eq(*_)
}
}
}
But I can't seem to find a way to do assertions about the interactions between the CarService and the criteria object.
What am I missing?
The Grails Where query instead of the Criteria query seems to be better choice for an idiomatic way to do this in Grails:
Gorm Where Query
I'm a bit new to Laravel unit testing. I need to get different outputs by calling same repo function for the unit testing.
So far my test is like this:
public function testReportOffdayWorked()
{
$input = [
'from_date' => '2016/01/01',
'to_date' => '2016/01/03',
];
$webServiceRepositoryMock = Mockery::mock('App\Repositories\WebServiceRepository');
$webServiceRepositoryMock->shouldReceive('callGet')->twice()->andReturn($this->issues);
$this->app->instance('App\Repositories\WebServiceRepository', $webServiceRepositoryMock);
$this->call('post', '/reporting/portal/report-offdays', $input);
$this->assertResponseOk();
$this->assertTrue($this->response->original->getName() == "Reporting::report_offday_worked");
}
I would like to get two different outputs for callGet function.
Set up a sequence of return values or closures for callGet().
andReturn(value1, value2, ...)
Sets up a sequence of return values or closures. For example, the first call will return value1 and the second value2. Note that all subsequent calls to a mocked method will always return the final value (or the only value) given to this declaration.
— docs.mockery
The following shows how to do it in PHPUnit mocks and mockery.
<?php
class The {
public function answer() { }
}
class MockingTest extends \PHPUnit_Framework_TestCase
{
public function testMockConsecutiveCalls()
{
$mock = $this->getMock('The');
$mock->expects($this->exactly(2))
->method('answer')
->will($this->onConsecutiveCalls(4, 2));
$this->assertSame(4, $mock->answer());
$this->assertSame(2, $mock->answer());
}
public function testMockeryConsecutiveCalls()
{
$mock = Mockery::mock('The');
$mock->shouldReceive('answer')->andReturn(4, 2);
$this->assertSame(4, $mock->answer());
$this->assertSame(2, $mock->answer());
}
}
How about using PHPUnit mocking framework?
$mock = $this->getMock('ClassName');
$mock->expects($this->at(0))
->method('getInt')
->will($this->returnValue('one'));
$mock->expects($this->at(1))
->method('getInt')
->will($this->returnValue('two'));
echo $mock->getInt(); //will return one
echo $mock->getInt(); //will return two
I am attempting to test a simple Laravel model which has required "password" and "email" properties. My test reads as follows…
public function testEmailIsRequired() {
$user = new User;
$user->password = 'derp';
// should not save
$this->assertFalse($user->save());
}
Rather than correctly agree that "this doesn't work" and a successful test, I’m getting…
1) UserTest::testEmailIsRequired
Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation:
19 NOT NULL constraint failed: users.email
(SQL: insert into "users" ("password", "updated_at", "created_at")
values (derp, 2014-09-26 15:27:07, 2014-09-26 15:27:07))
[...]
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
I’m afraid that I’m a total novice here. It seems to me that PHPUnit ought to be suppressing the database driver’s own error reporting and just accepting "false"… help?
Your test is looking for the function to return false. Any other errors generated in your function will still be returned, since you are calling the function.
public function testEmailIsRequired()
{
$user = new User;
$user->password = 'derp';
// should not save
$this->assertFalse($user->save());
}
public function testMockSaveWorks()
{
$Mock = $this->getMock('User', array('save'));
// Set up the expectation for the getResult() method
$Mock->expects($this->any())
->method('save')
->will($this->returnValue(true));
$this->assertTrue($Mock->save());
}
class User()
{
...
public function save()
{
if( is_null($this->email) )
return false;
...
return true;
}
}
You should then use Mocks to remove the actual save() function and its tie in to your database, and have the mock return true to pretend to save the record to show that you have tested the module.
I am using shiro security in my grail application.
Grails version : 2.2.1
shiro : 1.2.0
I have a problem in writing grails unit test case for the controller with filter enabled. When the test case run without filters then it is working fine, if it runs withFilters then it is failing for accessControl() method not found in the controller. I dont know how to make Shiro's api to be visible while running the test case.I referred shiro unit test case link http://shiro.apache.org/testing.html but I couldn't get any info regarding accessControl().I have given sample code how my classes and test case looks like
MyController.groovy
def create() {
// getting request parameters and validation
String emailId = params.emailId
def ret = myService.createUser(emailId)
return ret
}
MyControllerFilters.groovy
def filters = {
loginCheck(controller: 'user') {
before = {
//do some business checks
// Access control by convention.
accessControl() // This is a dynamic method injected by ShiroGrailsPlugin to FilterConfig, but this is not visible during the test case.
}
}
MyControllerTests.groovy
#TestFor(MyController)
#Mock(MyControllerFilters)
class MyControllerTests {
#Before
void setup() {
// initializing some variables
}
void testCreateUserWithFilter() {
request.accessAllowed = true
withFilters(action:"create") {
controller.create()
}
assert response.message == "success"
}
}
Try to use:
ControllerOrService.metaClass.method = {
return "" //what you need to be returned
}
Add parameters to closure if method take parameters:
ControllerOrService.metaClass.method = { def a, def b ->
return a + b
}
Don't forget to use full name of method when you mock them in that way.
I'm unit testing a Grails service and using Mocks to mock out calls to the
GrailsApplication class. I have one test that succeeds but when I try
subsequent tests they fail. I am using demand to mock the isDomainClass
method. I have tried copying and pasting the code from the test that
succeeds to the test method that fails but the second time the same code
runs it fails saying that no more calls to isDomainClass are expected. I'm
suspecting some leakage between the methods but I can't see where it is.
Things I've tried already:
Running the tests from the command line (I'm running the tests under SpringSource Tool Suite version 2.7.0.201105292341-M2.)
Moving the failing test to a different test class (the test that runs first succeeds)
Changing the number range in the demands clause to 1..5 (second test still fails)
Here is the relevant portions of my test case:
package simulation
import grails.test.*
import org.joda.time.*
import org.codehaus.groovy.grails.commons.GrailsApplication
class ObjectSerializationServiceTests extends GrailsUnitTestCase {
def objectSerializationService
protected void setUp() {
super.setUp()
objectSerializationService = new ObjectSerializationService()
}
protected void tearDown() {
super.tearDown()
objectSerializationService = null
}
void testDomainObjectSerialization() {
def otherControl = mockFor(GrailsApplication)
otherControl.demand.isDomainClass(1..1) {true}
otherControl.demand.getDomainClass(1..1) {className ->
assert className == "simulation.TestDomainClass"
TestDomainClass.class
}
objectSerializationService.grailsApplication = otherControl.createMock()
def now = new DateTime()
def testObject = new TestDomainClass([id:57, someOtherData:"Some Other
Data", theTime:now])
def testInstances = [testObject]
mockDomain(TestDomainClass, testInstances)
def serialized = objectSerializationService.serializeObject(testObject)
def deserialized =
objectSerializationService.deserializeObject(serialized)
assert deserialized == testObject
assert serialized.objectType == SerializedObject.ObjectType.DOMAIN
otherControl.verify()
}
void testSerializableSerialization() {
def otherControl = mockFor(GrailsApplication)
otherControl.demand.isDomainClass(1..1) {true}
otherControl.demand.getDomainClass(1..1) {className ->
assert className == "simulation.TestDomainClass"
TestDomainClass.class
}
objectSerializationService.grailsApplication = otherControl.createMock()
def now = new DateTime()
def testObject = new TestDomainClass([id:57, someOtherData:"Some Other
Data", theTime:now])
def testInstances = [testObject]
mockDomain(TestDomainClass, testInstances)
def serialized = objectSerializationService.serializeObject(testObject)
def deserialized =
objectSerializationService.deserializeObject(serialized)
assert deserialized == testObject
assert serialized.objectType == SerializedObject.ObjectType.DOMAIN
otherControl.verify()
}
}
And the output:
Testcase: testDomainObjectSerialization took 0.943 sec
Testcase: testSerializableSerialization took 0.072 sec
FAILED
junit.framework.AssertionFailedError: No more calls to 'isDomainClass'
expected at this point. End of demands.
at grails.test.MockClosureProxy.doBeforeCall(MockClosureProxy.java:66)
at grails.test.AbstractClosureProxy.call(AbstractClosureProxy.java:74)
at
simulation.ObjectSerializationService.serializeObject(ObjectSerializationService.groovy:20)
at simulation.ObjectSerializationService$serializeObject.call(Unknown
Source)
at
simulation.ObjectSerializationServiceTests.testSerializableSerialization(ObjectSerializationServiceTests.groovy:68)
I got a similar error trying to use mockFor on jms Message interface in multiple test cases.
I got around it by creating a custom interface that extends from the interface that needs to be mocked. You would use the custom interface to create the mock.
e.g.
private interface GrailsApplicationTest1 extends GrailsApplication(){}
testOne(){
def control = mockFor(GrailsApplicationTest1)
//...rest of code
}
private interface GrailsApplicationTest2 extends GrailsApplication(){}
testTwo(){
def control = mockFor(GrailsApplicationTest2)
//...rest of code
}
//add more private interfaces for additional test cases..
I'm not exactly sure why but I think the mockFor behaves differently between interfaces and non-interfaces. But that's just a wild guess.