4

I have a custom extension with model with structure like this in database:

+----------------------------------+
|entity_id | customer_id | balance |
+----------------------------------+
|     1    |      3      | 7100000 |
|     2    |      5      | 1250000 |
+----------------------------------+

I usually use my model in __construct function in controller like this:

public function __construct(
.... ,
/Namespace/Module/Model/CreditFactory $credit
... ,
){
  $this->credit = $credit;
}

and i can use to store and retrieve data from my model collection easily like this:

//retrieve Data
$credit = $this->credit->create()->load('1');
$anotherCredit = $this->credit->create()->getCollection()
    ->addFieldToFilter('customer_id','5')->getFirstItem();
$balance = $credit->getBalance();
$anotherBalance = $anotherCredit->getBalance();
//Store Data
$amount = 1000;
$endCreditBalance = $balance - $amount;
$credit->SetBalance(endCreditBalance);
$credit->save();
$endAnotherCreditBalance = $balance + $amount;
$anotherBalance->SetBalance(endAnotherCreditBalance);
$anotherBalance->save();

I tried to create unit test to compared the saved value with the value i want to store like this:

protected function setUp()
{
 $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
 $this->customerCredit = $objectManager->getObject(\Namespace\Module\Model\CreditFactory::class);      
}

public function testSend()
{
 $credit = $this->credit->create()->load('1');
 $anotherCredit = $this->credit->create()->load('2');

 $balance = $credit->getBalance();
 $anotherBalance = $anotherCredit->getBalance(); 
 $amount = 250;
 $endCreditBalance = $balance - $amount;
 $credit->SetBalance(endCreditBalance);
 $credit->save();
 $endAnotherCreditBalance = $balance + $amount;
 $anotherBalance->SetBalance(endAnotherCreditBalance);
 $anotherBalance->save();
 $result = array(
          'credit_balance' => $credit->getBalance(),
          'another_credit_balance' => $anotherCredit->getBalance()
 );
 $expected = array(
          'credit_balance' => $endCreditBalance,
          'another_credit_balance' => $endAnotherCreditBalance
 );
 $this->assertEquals($expected, $result);
}

but i always got an error like this:

Error: Call to a member function load() on null

and i don't know if the rest of the code will work just fine or not

4
  • 1
    If you want to test database interaction you should take a look at creating an integration test instead. Also you are trying to load particular credits (ids 1 and 2) which you would need to add as a fixture first before being able to load them in a test. May 7, 2018 at 10:59
  • @KristofatFooman can you give an example for that one?
    – mileven
    May 7, 2018 at 11:00
  • 2
    I wouldn't be able to provide an example but this article gives a good overview of what is involved integer-net.com/integration-tests-with-magento-2 May 7, 2018 at 11:05
  • You want to run Unit test or integration test? @mileven
    – Keyur Shah
    May 7, 2018 at 13:23

2 Answers 2

0

What you are trying to do is actually an integration test and not an unit test. Magento makes an clear distinct in the types of test. With unit tests, all the dependencies of the class you are trying to test are replace with mocks, where all functions return null by default. It is also impossible to access the database as there is no active connection.

With integration tests however you get access to the actual objects and you can access the database.

In your code that is what you actually are trying to do: Testing if the integration between your database and your code is correct.

0

The reason for the error is that you have instantiated rather than mocked the factory object. You need to mock the factory and have it return the object you need to call load on.

protected function setUp()
{
    $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
    $this->creditFactory = $this->createMock(\Namespace\Module\Model\CreditFactory::class);
    $this->creditFactory->method('create')->willReturnCallback(function(){
        return $this->objectManager->getObject(\Namespace\Module\Model\Credit::class); 
    });
}

So when you call load on the object returned by mock factory it is not null,

public function testSend()
{
    $credit = $this->creditFactory->create()->load('1');

You also need to add data to the objects to run tests on them. Since unit tests cannot call the database for data, you need to populate your models with data so you can run tests on them.

    $anotherCredit = $this->creditFactory->create()->load('2');
    $credit->setBalance(2000);
    $anotherCredit->setBalance(3000);

You may also need to mock the resource models, you can figure that one out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.