Unit testing functions with side effects?

旧城冷巷雨未停 提交于 2019-12-07 03:32:18

问题


Let's say you're writing a function to check if a page was reached by the appropriate URL. The page has a "canonical" stub - for example, while a page could be reached at stackoverflow.com/questions/123, we would prefer (for SEO reasons) to redirect it to stackoverflow.com/questions/123/how-do-i-move-the-turtle-in-logo - and the actual redirect is safely contained in its own method (eg. redirectPage($url)), but how do you properly test the function which calls it?

For example, take the following function:

function checkStub($questionId, $baseUrl, $stub) {
  canonicalStub = model->getStub($questionId);
  if ($stub != $canonicalStub) {
    redirectPage($baseUrl . $canonicalStub);
  }
}

If you were to unit test the checkStub() function, wouldn't the redirect get in the way?

This is part of a larger problem where certain functions seem to get too big and leave the realm of unit testing and into the world of integration testing. My mind immediately thinks of routers and controllers as having these sorts of problems, as testing them necessarily leads to the generation of pages rather than being confined to just their own function.

Do I just fail at unit testing?


回答1:


You say...

This is part of a larger problem where certain functions seem to get too big and leave the realm of unit testing and into the world of integration testing

I think this is why unit testing is (1) hard and (2) leads to code that doesn't crumble under its own weight. You have to be meticulous about breaking all of your dependencies or you end up with unit tests == integration tests.

In your example, you would inject a redirector as a dependency. You use a mock, double or spy. Then you do the tests as @atk lays out. Sometimes it's not worth it. More often it forces you to write better code. And it's hard to do without an IOC container.




回答2:


This is an old question, but I think this answer is relevant. @Rob states that you would inject a redirector as a dependency - and sure, this works. However, your problem is that you don't have a good separation of concerns.

You need to make your functions as atomic as possible, and then compose larger functionality using the granular functions you've created. You wrote this:

function checkStub($questionId, $baseUrl, $stub) {
  canonicalStub = model->getStub($questionId);
  if ($stub != $canonicalStub) {
    redirectPage($baseUrl . $canonicalStub);
  }
}

I'd write this:

  function checkStubEquality($stub1, $stub2) {
    return $stub1 == $stub2;
  }

  canonicalStub = model->getStub($questionId);
  if (!checkStubEquality(canonicalStub, $stub)) redirectPage($baseUrl . $canonicalStub);



回答3:


It sounds like you just have another test case. You need to check that the stub is identified correctly as a stub with both positive and negative testing, and you need to check that the page to which you are redirected is correct.

Or do I totally misunderstand the question?



来源:https://stackoverflow.com/questions/3589336/unit-testing-functions-with-side-effects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!