3

I have the following resolve function in one of my classes:

public function __construct( 
   \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
   \Magento\Framework\Webapi\ServiceInputProcessor $serviceInputProcessor)
    {
        $this->categoryRepository = $categoryRepository;
        $this->serviceInputProcessor = $serviceInputProcessor;
    }

public function resolve($value, array $args, ResolveInfo $info)
{
    $inputParams = $this->serviceInputProcessor->process(\Magento\Catalog\Api\CategoryRepositoryInterface:class, 'save', $args);
    $result = $this->categoryRepository->save($inputParams[0]);
    return $result;
}

In order to test it I have created the following test case:

public function testResolve()
    {
        $categoryRepoMock = $this->getMockBuilder(\Magento\Catalog\Model\CategoryRepository::class)
            ->disableOriginalConstructor()
            ->getMock();

        $serviceInputProcessorMock = $this->getMockBuilder(\Magento\Framework\Webapi\ServiceInputProcessor::class)
            ->disableOriginalConstructor()
            ->getMock();

        $field = new AddCategoryField($categoryRepoMock, $serviceInputProcessorMock);

        // do asserts here
    }

What could I assert there in order to do a proper unit testing?

Should I do unit testing at all?

I am not invoking any my own function, so maybe I can omit unit testing and perform only integration tests?

2 Answers 2

6

In addition to @raphael-at-digital-pianism's great response.

I find the resolve method rather complex, so I would want test coverage.

$inputParams = $this->serviceInputProcessor->process( \Magento\Catalog\Api\CategoryRepositoryInterface:class, 'save', $args ); $result = $this->categoryRepository->save($inputParams[0]); return $result;

Doing TDD (e.g. writing the tests first), I probably would have written the following unit tests:

  1. Check that process is called with the expected arguments
  2. Check that save is called with the first array value of the process return value
  3. That the method returns the result of the save method.

Since the code already exists however, and I'm sure you manually have confirmed it is doing those things listed above, I see little value in writing the unit tests now. The only reason would be to practice writing unit tests so next time you might be able to writing them first.

Depending on how important and valuable the function for your project is, I would definitely consider still writing an integration test however.

To do so I'd probably avoid using test doubles (mocks), and instead wrap the whole test in a transaction using the @MagentoDbIsolation enabled integration test framework annotation.

And finally, regarding the testability of your method you might want to consider redesigning it so it either returns a value (return $result) or causes a side effect ($category->save()). I found that kind of separation leads to a cleaner design of my classes and my code tends to be more maintainable and easy to test that way.

I don't know what you are doing with the returned $result, but since many return values are added "just in case", the easiest thing might be to not return anything at all (but that depends on your calling code of course).

I am lacking context knowledge, but on that note, given the method name is resolve(), I was surprised to see that it saves a category.
Maybe giving it a more descriptive name also helps in figuring out what a test should be doing.

6
  • 1
    Great response! Marked as the most helpful due to showing TDD test examples. resolve function name is forced, it must be there. Do you suggest putting return $category->save() to a separate method? If yes, what about then with return @serviceInputProcessor->process()? Leave it? Should it be copied together with $category-save()? Or is it better to put to next another method? What is the best from unit tests point of view? In fact, only three lines of code in the function should not be a problem.
    – old_user
    Commented Jul 30, 2016 at 11:02
  • 1
    What interface defines the resolve() method you are implementing? If it defines a @return annotation, then you don't have a choice unfortunately. Otherwise, just don't return anything (that is, match the PHPDoc annotation @return void).
    – Vinai
    Commented Jul 30, 2016 at 11:07
  • 1
    Sorry for the late response. There is @return mixed. But in fact, I have to return something. It is a part of API ;)
    – old_user
    Commented Aug 2, 2016 at 15:18
  • 1
    Out of curiosity, what api interface are you implementing?
    – Vinai
    Commented Aug 2, 2016 at 17:57
  • 1
    I will publish it soon and probably ask you for help :)
    – old_user
    Commented Aug 5, 2016 at 11:10
3

Well its up to you to decide whether or not you should do integration or unit tests.

Here my suggestion: why not do both?

Thanks to what Vinai taught me here's how I see things:

Try to do unit tests everytime you write new code. Even better you start your development by writing the test first and then start developing based on your test. That's one of the practice of test driven development

Write integrations tests for existing code and new code. Integrations tests ensure your code is working well with the rest of the system while unit tests ensure each unit of your code is behaving properly.

You can have all your units tests passing and still integration tests could fail.

Keep in mind that the more tests you write the easier your code will be debugable and you're reducing the risks of bugs with tests because they help you find them before the end of your development.

It's definitely better to spend half a day writing proper tests than spend half way looking for a bug that would have been avoided with your tests.

Again it's your choice to decide what you want to do and people here will just be able to give you their opinions.

0

Your Answer

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

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