say I have two models that extend from Eloquent and they relate to each other. Can I mock the relationship?
ie:
class Track extends Eloquent {
public function courses()
{
return $this->hasMany('Course');
}
}
class Course extends Eloquent {
public function track()
{
return $this->belongsTo('Track');
}
}
in MyTest, I want to create a mock of course, and return an instance of track, by calling the track property, not the track instance (I don't want the query builder)
use \Mockery as m;
class MyTest extends TestCase {
public function setUp()
{
$track = new Track(array('title' => 'foo'));
$course = m::mock('Course[track]', array('track' => $track));
$track = $course->track // <-- This should return my track object
}
}
Since track is a property and not a method, when creating the mock you will need to override the setAttribute and getAttribute methods of the model. Below is a solution that will let you set up an expectation for the property you're looking for:
$track = new Track(array('title' => 'foo'));
$course = m::mock('Course[setAttribute,getAttribute]');
// You don't really care what's returned from setAttribute
$course->shouldReceive('setAttribute');
// But tell getAttribute to return $track whenever 'track' is passed in
$course->shouldReceive('getAttribute')->with('track')->andReturn($track);
You don't need to specify the track method when mocking the Course object, unless you are also wanting to test code that relies on the query builder. If this is the case, then you can mock the track method like this:
// This is just a bare mock object that will return your track back
// whenever you ask for anything. Replace 'get' with whatever method
// your code uses to access the relationship (e.g. 'first')
$relationship = m::mock();
$relationship->shouldReceive('get')->andReturn([ $track ]);
$course = m::mock('Course[track]');
$course->shouldReceive('track')->andReturn($relationship);
Related
I've started unit testing the first time. I'm following tutorials of resoCoder
here is my test code where I'm mocking my dbManager class but I could not mock DAO's as they are auto-generated in moor and there is no setter method for them.
class MockDbManager extends Mock implements DbManager{}
void main() {
RecipeLocalDataSource dataSource;
MockDbManager _dbManager;
setUp(() {
_dbManager = MockDbManager();
dataSource = RecipeLocalDataSource(_dbManager);
});
group('Search Food Table', (){
List<FoodTableData> getFoodTable(){
var list = [];
for(var i =1; i <=5 ; i++){
list.add(FoodTableData(id: i, name: 'item $i'));
}
return list;
}
var searchQuery = 'query';
test('Should return foodTableData when query is successful', (){
//arrange
when(_dbManager.foodTableDao.searchFoods(searchQuery)).thenAnswer((realInvocation) async => getFoodTable());
//act
var result = dataSource.searchFoodTable('test');
//assert
verify(_dbManager.foodTableDao.searchFoods(searchQuery));
expect(getFoodTable(), result);
});
});
}
I'm getting the following error
NoSuchMethodError: The method 'searchFoods' was called on null.
Receiver: null
Tried calling: searchFoods("query")
I understood the error but don't know how to solve that.
Also, I'm getting a similar issue with preferenceManager class as well, where I'm having a getter for UserPrefs.
UserPrefs get user => UserPrefs(_pref);
when I'm accessing _prefManager.user.name for the test, it throws the same error. How can I tackle that as well?
I believe you're missing a level of mocking, leading to the null exception. Keep in mind the mocked class returns null for everything. You have to provide values for everything.
You've mocked DbManager, but not the foodTableDao field inside DbManager.
// I don't have your classes, so these are just my guesses at matching their interface
abstract class TableDao {
String searchFoods();
}
abstract class DbManager {
TableDao foodTableDao;
}
class MockDbManager extends Mock implements DbManager {}
class MockTableDao extends Mock implements TableDao {}
// ↑ Define this mocked class as well
void main() {
test('Mockito example', () {
final dbManager = MockDbManager();
final mockTableDao = MockTableDao();
// ↑ instantiate this mocked class which will be a field value inside dbManager
// Mock the field access for dbManager.foodTableDao
when(dbManager.foodTableDao).thenReturn(mockTableDao);
// ↑ You're missing this ↑ level of stubbing, so add this
when(mockTableDao.searchFoods()).thenAnswer((realInvocation) => 'Cucumber');
// ↑ define the stubbing at the mockTableDao level, not dbManger.mockTableDao.searchFoods
expect(dbManager.foodTableDao.searchFoods(), 'Cucumber');
// You can use foodTableDao field here, getting the mocked value as expected
});
}
In your example above, you can't access dbManager.foodTableDao to set up a mock for dbManager.foodTableDao.searchFoods(), because dbManager.foodTableDao is null until you mock it. So this line:
when(_dbManager.foodTableDao.searchFoods(searchQuery)).thenAnswer((realInvocation) async => getFoodTable());
was throwing the null exception, not the expect test below. (The expect test was never reached.)
The issue with _prefManager.user.name is the same I'm guessing. You need to mock User class and provide a when/return of MockUser for _prefManager.user in order to provide another level of when/return for _prefManager.user.name.
It's like Mockception... you need to go another level deeper in your mocks. ;)
I'm building a Laravel 5 application at the moment and have gotten myself confused about how to mock things in PhpSpec.
I'm building a schedule times validator that requires the intended schedule to be checked against all current schedules and see if there's any overlap (events are not allowed to overlap).
I need to pull in the schedules in question so I can test against them. At the moment it's a very basic whereBetween query, but it's going to get a lot more complicated as there'll be recurring schedules to check against as well.
So here's my stripped down class. I really just want to test the doesNotOverlap function.
use App\Schedule;
class ScheduleTimesValidator
{
protected $schedule;
public function __construct(Schedule $schedule)
{
$this->schedule = $schedule;
}
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->getSchedulesBetween($slug, $intended);
if(empty($schedules)) return true;
return false;
}
protected function getSchedulesBetween($slug, $intended)
{
// Casting to array to make testing a little easier
return $this->schedule->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']])
->get()->toArray();
}
and here's my Spec
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ScheduleTimesValidatorSpec extends ObjectBehavior
{
protected $validIntended = [
'start' => '2015-12-01 12:00:00',
'end' => '2015-12-01 13:00:00'
];
protected $churchNonRecurringSchedules = [
['start' => '2014-11-20 13:00:00', 'end' => '2014-11-21 14:00:00'],
['start' => '2014-11-23 10:36:07', 'end' => '2014-11-23 11:36:07'],
];
function let($schedule)
{
$schedule->beADoubleOf('App\Schedule');
$this->beConstructedWith($schedule);
}
function it_is_initializable()
{
$this->shouldHaveType('App\Validation\ScheduleTimesValidator');
}
function it_should_return_true_if_it_does_not_overlap($schedule)
{
// $schedule->any()->willReturn([]);
// $schedule->whereIsRecurring()->shouldBeCalled();
// $schedule->whereIsRecurring(false)->ofChurch()->whereBetween()->get()->toArray()->willReturn([]);
// $schedule->willReturn([]);
// $this->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
// Tear Down
function letgo() {}
}
If I run it like that I get:
! it should return true if it does not overlap
method 'Double\App\Schedule\P8::whereIsRecurring()' not found.
I tried (as you can see) various commented out things to mock what $schedule will return, but that doesn't seem to work.
So I guess I want to mock the protected getSchedulesBetween method in the class, but doing things like $this->getSchedulesBetween($arg, $arg)->willReturn(blah) doesn't work.
Do I need to pull getSchedulesBetween() out of the class and move it into another class and then mock that? Or do I need to push $this->schedule->blah into the doestNotOverlap method so I can mock what $schedule will return?
I don't want to actually test the App\Schedule Laravel Model - I just want to mock what it's returning and will be hardcoding a variety of queries that will be run to get the different model results.
End of a long day here so brain a little zonked.
Update 2014-10-23
So I created a scope on my Schedule model
public function scopeSchedulesBetween($query, $slug, $intended)
{
return $query->whereIsRecurring(false)
->ofChurch($slug)
->whereBetween('start', [$intended['start'], $intended['end']]);
}
Then created a new App\Helpers\ScheduleQueryHelper which instantiated App\Schedule as a variable and added this method:
public function getSchedulesBetween($slug, $intended)
{
return $this->schedule->schedulesBetween($slug, $intended)->get()->toArray();
}
Then updated my spec to do
function let($scheduleQueryHelper)
{
$scheduleQueryHelper->beADoubleOf('App\Helpers\ScheduleQueryHelper');
$this->beConstructedWith($scheduleQueryHelper);
}
function it_should_return_true_if_it_does_not_overlap($scheduleQueryHelper)
{
$scheduleQueryHelper->getSchedulesBetween('slug', $this->validIntended)->willReturn([]);
$this->doesNotOverlap('slug', $this->validIntended)->shouldReturn(true);
}
And back in my ScheduleTimesValidator class did
public function doesNotOverlap($slug, $intended)
{
$schedules = $this->scheduleQueryHelper->getSchedulesBetween($slug, $intended);
if(empty($schedules)) {
return true;
}
return false;
}
And now PhpSpec is mocking that other class ok. However this seems like a very roundabout way to be doing things.
I need to test a repository, which has a Eloquent model injected via constructor.
class EloquentOrderRepository implements OrderRepositoryInterface
{
protected $model;
public function __construct(Order $model)
{
$this->model = $model;
}
public function calculateValues(array $deliveryOption = null)
{
if (! is_null($deliveryOption)) {
$this->model->value_delivery = (float) number_format($deliveryOption['price'], 2);
}
$this->model->value_products = (float) number_format($this->model->products->getTotal(), 2);
$this->model->value_total = (float) $this->model->value_products + $this->model->value_delivery;
}
}
My problem is when I call $this->model->value_products (or any of the attributes). The Eloquent model try to call the setAttribute method, which doesn't exist on the mocked model. If I mock this method, I can't set the attribute correctly, and my test assertions will fail.
Here is my test:
<?php
class EloquentOrderRepositoryTest extends \PHPUnit_Framework_TestCase
{
protected $model, $repository;
public function setUp()
{
$this->model = Mockery::mock('Order');
}
public function test_calculate_values()
{
$repository = new EloquentOrderRepository($this->model);
$this->model->products = m::mock('SomeCollection');
$this->model->products->shouldReceive('getTotal')->once()->withNoArgs()->andReturn(25);
$this->model->calculateValues(array('price' => 12));
$this->assertEquals(12, $this->model->value_delivery);
$this->assertEquals(25, $this->model->value_products);
$this->assertEquals(37, $this->model->value_total);
}
}
Any thoughts on this?
I think your main issue is that you're not using the repository pattern correctly. You should think about the passed model in your constructor as a prototype. It's not a real thing to be worked with, but an instance of something you use for other things. In the repository, you may have a method getUnpaidOrders which will do something like return $this->model->wherePaid('0')->get();. As you can see, we're not interacting with the instance as an actual concrete instance but more of something to achieve a broader scope.
In your calculate method you're actually setting values on this prototype model. I don't know what you then intend to do with these but as far as I'm aware this is not what the repository patter is supposed to do. The methods on a repository are generally static-like methods, where you call them (maybe with some input) and get something back. They shouldn't have an effect on any kind of internal state as a repository shouldn't have any kind of internal state.
Hopefully this makes sense.
Let's say I have a small class that handles connections to MySQL databases:
class myDatabaseHandler{
private $_databases;
public function addDatabase($name, $dsn, $username, $password){
$this->_databases[$name] = array('dsn' => $dsn, 'username' => $username, 'password' => $password);
return true;
}
public function getDatabase($name){
$connectionInfo = $this->_databases[$name];
$database = new Database($connectionInfo['dsn'], $connectionInfo['username'], $connectionInfo['password']);
$database->doSomeSetup();
$database->attachMoreThings();
return $database;
}
}
I want to unit test these 2 methods:
class myDatabaseHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testAddDatabase(){
}
public function testGetDatabase(){
}
}
How can this I test those 2 methods? If I addDatabase(), at most it would return a Boolean telling me the operation succeeded. Since it writes to a private property, I cannot confirm that the correct data is indeed written to it.
I feel that using getDatabase() to get a Database object back and testing against it is not exactly ideal, because I would need to expose dsn, username and password just for the sake of testing. In addition, it is possible that the Database object might modify those values to a format it uses, so I need to store the original values just for testing.
What is the best way to approach this problem?
Testing certainly gets tricky when you try to construct and use an object in the same place. In this case, you are both constructing a Database and calling methods on it in your getDatabase($name) method. That makes it pretty much impossible to mock, for instance, and to get decent coverage your tests would need to test the functionality provided by the Database class to make sure the system was behaving as expected.
A better way might be using a proper factory as a dependency.
interface iDatabaseFactory
{
public function buildDatabase($dsn, $username, $password);
}
Then, you could mock both the database factory and the database instance itself to verify that it is both constructed correctly and initialized correctly:
class MockDatabaseFactory implements iDatabaseFactory
{
public $databaseParams = array();
public $databaseToReturn = NULL;
public function buildDatabase($dsn, $username, $password)
{
$this->databaseParams['dsn'] = $dsn;
$this->databaseParams['username'] = $username;
$this->databaseParams['password'] = $password;
return $this->databaseToReturn;
}
}
class myDatabaseHandlerTest extends PHPUnit_Framework_TestCase
{
public function testAddAndGetDatabaseUsesCorrectDbParameters(){
$mockDatabaseFactory = new MockDatabaseFactory();
$dbHandler = new myDatabaseHandler($mockDatabaseFactory);
// implement MockDatabase according to your interface
$mockDatabase = new MockDatabase();
$mockDatabaseFactory->databaseToReturn = $mockDatabase;
$dbHandler.addDatabase("some name", "some dsn",
"some username", "pa$$w0rd");
$builtDatabase = $dbHandler.getDatabase("some name");
$this->assertEquals($mockDatabase, $builtDatabase);
$dbParams = $mockDatabaseFactory->databaseParams;
$this->assertEquals("some dsn", $dbParams['dsn']);
$this->assertEquals("some username", $dbParams['username']);
$this->assertEquals("pa$$w0rd", $dbParams['password']);
}
public function testAddAndGetDatabaseInitializesDb(){
$mockDatabaseFactory = new MockDatabaseFactory();
$dbHandler = new myDatabaseHandler($mockDatabaseFactory);
$mockDatabase = new MockDatabase();
$mockDatabaseFactory.setDatabaseToBuild($mockDatabase);
$dbHandler.addDatabase("name", "dsn", "user", "pass");
$builtDatabase = $dbHandler.getDatabase("some name");
$this->assertTrue($mockDatabase->doSomeSetupWasCalled);
$this->assertTrue($mockDatabase->attachMoreThingsWasCalled);
}
}
I'm trying to get my controller in a unit test to return a mock file when Request.Files[0] is called
From other posts on this site I've put together:
[TestMethod]
public void CreateFileInDatabase()
{
var repository = new MocRepository();
var controller = GetController(repository);
HttpContextBase mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
HttpRequestBase mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockHttpContext.Stub(x => x.Request).Return(mockRequest);
mockRequest.Stub(x => x.HttpMethod).Return("GET");
var filesMock = MockRepository.GenerateMock<HttpFileCollectionBase>();
var fileMock = MockRepository.GenerateMock<HttpPostedFileBase>();
filesMock.Stub(x => x.Count).Return(1);
mockRequest.Stub(x => x.Files).Return(filesMock);
var t = mockHttpContext.Request;
var automobile = new Automobile{ automobileNumber = "1234" };
controller.ControllerContext = new ControllerContext(mockHttpContext, new RouteData(), controller);
controller.Create(automobile);
}
When I'm in the controller during a test and call Request.Files I get the filesMock great.
However I want to be able to call Request.Files[0] and get a mock File which I can pass as a parameter to a method.
I haven't done much mocking before so any help would be appreciated!
Your filesMock object is a mock, therefore it have no idea how to resolve Files[0]. You need to tell it what to return, if someone asks for the first file:
filesMock.Stub(x => x[0]).Return(fileMock);
Add the above line after the creation of the fileMock object and the code works :)
The easiest way is to add an abstraction over the Request.Files[0] via an interface and object that actually calls out to Request.Files[0]
Note: I'm not actually sure what the datatype is for Request.Files[0], just using IFile as an example.
public interface IFileRetriever
{
IFile GetFile();
}
public class FileRetriever
{
public IFile GetFile()
{
return Request.Files[0];
}
}
Obviously suit the interface and real implementation to your use case, it will probably not be what is above...
In your class that currently calls out to Request.Files[0] just take in the IFileRetriever as a dependency, which is straightforward to mock out in Rhino Mocks (or any mocking/faking framework)