简体   繁体   English

PHPUnit:测试执行CRUD的REST API的更好方法

[英]PHPUnit: Better ways to test REST APIs which does CRUD operation

I have created one REST API, which does CRUD operation on DB and returns the result from DB. 我创建了一个REST API,该API在DB上执行CRUD操作并从DB返回结果。 Now, this is my below code: 现在,这是我的以下代码:

class CreateRecords
{
    public function createEntities($details)
    {
        if (trim($details['username']) == "") {
            $this->result = "Username is empty.";
        } else {
            $this->result = create_record($userDetails);
        }       
        return $this->result;
    }
}

Here, in this above code, create_record is a function which does validation part and insert the details into db and returns the id of the newly created record. 这里,在上面的代码中, create_record是一个函数,它进行验证,然后将详细信息插入db并返回新创建的记录的id。

Now, currently my test case is like this: 现在,目前我的测试用例是这样的:

class TestCreateRecords
{   
    public function setUp()
    {
        $this->client = new GuzzleHttp\Client([
                'base_uri' => 'http://localhost/test/'
            ]);
    }

    public function testCreateEntitiesAPI()
    {           
        $response = $this->client->post('service.php', [
            'json' => [
                'username' => 'test@test.com',
                'user_password' => 'test',
                'first_name' => 'Test',
                'last_name' => 'test',
                'email1' => 'test@test.com'
            ]
        ]);

        $actual_response = json_decode($response->getBody(), true);

        $this->assertArrayHasKey("id", $actual_response, 'Un-successful.');
    }
}

So, I have few queries regarding this: 因此,我对此有几个疑问:

  1. In my main class file, create_record function validates the username before inserting details into DB. 在我的主类文件中, create_record函数会在将详细信息插入数据库之前验证username It checks if the username is already available or not. 它检查username是否已经可用。 If available, it won't create a record and sends response as Already Available . 如果可用,它将不会创建记录并以Already Available发送响应。 If not available, then it will create a record and sends newly created unique id. 如果不可用,则它将创建一条记录并发送新创建的唯一ID。

    Now, I want to test both the scenarios, where passed username is already available in DB and not available in DB. 现在,我要测试两种情况,其中传递的username在DB中已经可用,而在DB中不可用。 Now, with my above test case, I'll have to make changes in it every time before running the test, so I can test the scenario, where username is already not available in DB. 现在,使用我上面的测试用例,每次运行测试前我都必须对其进行更改,因此我可以测试场景,其中username在DB中已经不可用。 And it seems illogical to me. 在我看来,这是不合逻辑的。

    I would like to know, is there any other way I can accomplish this? 我想知道,还有其他方法可以做到这一点吗?

  2. Someone advised me to mock the create_record method to test the API and write another test case to test the create_record method itself. 有人建议我模拟create_record方法来测试API,并编写另一个测试用例来测试create_record方法本身。 But as per my application MVC framework, it seems impossible for me, right now. 但是按照我的应用程序MVC框架,对于我来说,现在似乎是不可能的。 Because in create_record method, there are dozens of methods which are being called and these methods calls few others methods. 因为在create_record方法中,有数十种方法被调用,而这些方法却很少调用其他方法。 And again, I'll have to mock those dozens methods and write the test cases for them and so on. 同样,我将不得不模拟这几十种方法并为其编写测试用例,依此类推。

    It is time and effort consuming for us. 这对我们来说是费时和费力的。 So I have decided to not to mock any methods and test the whole API flow in one test case. 因此,我决定不模拟任何方法,而是在一个测试用例中测试整个API流程。

    I would like to know, is it a good approach? 我想知道,这是一个好方法吗? And what will be the side-effects of it? 它将带来哪些副作用?

Whoever advised you to try mocking is right. 谁建议您尝试嘲笑是对的。 It's great that you want to start testing more, but it often turns up problems in application architecture. 您希望开始进行更多测试非常棒,但这通常会导致应用程序体系结构出现问题。

Unit tests should ideally test just a single class or function. 理想情况下,单元测试应该只测试单个类或功能。 If all of your functions are calling other functions all the way down to the database insertion it's an end-to-end test. 如果您所有的函数都一直在调用其他函数,直到数据库插入,这就是端到端测试。 It's fine to have these, but in your case you want to test a variety of cases (when validation fails because username exists) and you'll soon find that testing all your edge cases in end-to-end tests is slow . 拥有这些很好,但是在您的情况下,您想测试各种情况(由于用户名存在而导致验证失败),您很快就会发现在端到端测试中测试所有边缘情况的速度

You already have a CreateRecords class. 您已经有一个CreateRecords类。 Where is this used in the service.php class? 这在service.php类中的哪里使用? If you're using some sort of dependency injection you can mock the CreateRecords class in the controller (which I'm guessing is service.php for you. In this way you can test the service separately from the API code. Here's an example: 如果您使用某种类型的依赖注入,则可以在控制器中模拟CreateRecords类(我猜是为您提供了service.php 。通过这种方式,您可以将服务与API代码分开进行测试。下面是一个示例:

/**
 * Controller action
 */
function myAction() {
  if ($this->isAdmin()) {
    throw new \Exception('You need to be an admin');
  }

  $this->userService->create($_POST);
}

/**
 * Service containing actions related to users
 */
class UserService {

  /**
   * @var UserValidator
   */
  protected $validator;

  /**
   * @var RecordSaver
   */
  protected $recordSaver;

  /**
   * @param UserValidator $validator
   * @param RecordSaver $recordSaver
   */
  public function __construct(
    UserValidator $validator, 
    RecordSaver $recordSaver
  ) {
    $this->validator = $validator;
    $this->recordSaver = $recordSaver;
  }

  /**
   * @param $params
   * @return array
   */
  public function create($params) {
    $this->validator->validate($params);

    return $this->recordSaver->save($params);
  }
}

/**
 * Validation of new users / or updating existing
 */
class UserValidator {
  /**
   * @param array $params
   * @throws Exception
   */
  public function validate($params) {
    if (!isset($params['username'])) {
      throw new \Exception('Username must be set');
    }
  }
}

/**
 * Wrapper for database calls
 */
class RecordSaver {
  /**
   * @param array $params
   * @return array
   */
  public function save($params) {
    return create_record($params);
  }
}

Then you can create tests that test each layer only, eg 然后,您可以创建仅测试每个层的测试,例如

  • A test that checks if the API will throw an exception if not an admin 如果API不是管理员,则检查API是否会引发异常的测试
  • A lot of different unit tests for user validation 许多不同的单元测试,用于用户验证
  • A few tests for user creation in UserService (mocking the validator and record saver) 在UserService中创建用户的一些测试(模拟验证程序和记录保护程序)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM