英文:
How to organize test classes with interdependencies?
问题
我有多个测试类和测试用例,这些类中的测试用例依赖于其他类的测试用例。我正在为OpenSearch
编写Integration Test
。
我有一个名为CreatePostTest
的类,用于测试在OpenSearch
中创建文档。然后,我有一个名为UpdatePostTest
的类,用于测试更新OpenSearch
中的现有文档。因此,为了更新文档,我首先运行CreatePostTest
,并获取新文档的ID,然后在UpdatePostTest
中使用它。
我尝试使用@depends
注释,但出现错误:“此测试依赖于不存在的“App\Tests\Integration\Api\Post\UpdatePostTest::CreatePostTest””。
我有多个具有相互依赖关系的类,我不想将所有测试用例放在同一个测试类中。
如何最好地组织这些测试用例?
英文:
I have multiple test classes and test cases in these classes depends on test case from other classes. I am writing Integration Test
for OpenSearch
.
I have a CreatePostTest
class which tests the creating a document in OpenSearch
. Then I have UpdatePostTest
class which tests the updating the existing document in OpenSearch
. So for updating the document I first run CreatePostTest
and grab the ID of new document and use it in UpdatePostTest
class CreatePostTest
{
public function testCreateDocument(): string
{
$postMock = $this->getBaseMockData();
$response = $this->getCreatePostRequest($postMock);
self::assertResponseSuccessful($response);
$post = $response['data']['createPost']['post'];
return $post['_id'];
}
//Other test cases
}
class UpdatePostTest
{
public function testUpdateDocument(string $postId)
{
$postMock = $this->getBaseMockData();
$postMock['postId'] = $postId;
$response = $this->getUpdatePostRequest($postMock);
self::assertResponseSuccessful($response);
}
//Other test cases
}
I have tried using @depends
annotation but that give me error This test depends on "App\Tests\Integration\Api\Post\UpdatePostTest::CreatePostTest" which does not exist.
I have multiple such classes with interdependencies and I don't want to put all test cases in the same Test class.
What is the best way to organize such test cases?
答案1
得分: 1
你无法为类设置依赖关系。但是,使用测试套件,你可以定义顺序。
这是一个API测试的示例。SecurityControllerTest生成一个在UserControllerTest中需要的JWT令牌。
<testsuites>
<testsuite name="首要测试">
<file>tests/Controller/SecurityControllerTest.php</file>
<file>tests/Controller/UserControllerTest.php</file>
</testsuite>
<testsuite name="后续测试">
<directory>tests</directory>
</testsuite>
</testsuites>
根据评论中的问题更新我的答案:
我使用Symfony进行API开发。API是无会话的。测试是ApiTestCases。我使用Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage来在测试期间存储JWT令牌。这意味着我不必自己实现基于文件的缓存/存储。使用MockFileSessionStorage,我有一个键值存储,不需要执行任何其他操作。
对我来说,使用缓存组件(FilesystemAdapter或ArrayAdapter)工作量太大。在我看来,ArrayAdapter也必须集成到Singleton中才能正常工作。
namespace App\Tests\Mock;
use Symfony\Component\HttpFoundation\Session\Session;
class MockFileSessionStorage
{
public static function getSession(): Session
{
$session = new Session(
new \Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage(
realpath(__DIR__ . '/../var')
)
);
$session->setId('key');
return $session;
}
}
在成功认证后,JWT令牌位于响应主体中的测试方法中。
// SecurityControllerTest
public function testAuthSuccessAndIsJwtTokenInResponseBody()
{
$session = MockFileSessionStorage::getSession();
$session->clear();
// 做一些操作
$responseData = json_decode($response->getContent(), true);
$session->set('jwt_token', $responseData['token']);
$session->save();
}
这是其他测试控制器也需要的一个抽象类方法。
// AbstractController
public function getJwtAuthHeader(array $options): array
{
if (false === isset($options['headers'])) {
$options['headers'] = [];
}
$session = MockFileSessionStorage::getSession();
$options['headers']['Authorization'] = sprintf('Bearer %s', $session->get('jwt_token'));
return $options;
}
以及在UserControllerTest中的调用。
// UserControllerTest
public function testUpdateAction(): void
{
$options = [];
$options = $this->getJwtAuthHeader($options);
$response = static::createClient()->request('PUT', '/api/users', $options);
// 做一些操作
}
英文:
You cannot set Dependencies for classes. However, using a testsuite, you can define the order.
Here is an example of an API test. The SecurityControllerTest generates a JWT token that is needed in the UserControllerTest.
<testsuites>
<testsuite name="First Tests">
<file>tests/Controller/SecurityControllerTest.php</file>
<file>tests/Controller/UserControllerTest.php</file>
</testsuite>
<testsuite name="Following Tests">
<directory>tests</directory>
</testsuite>
</testsuites>
Update my answer because of a question in the comment:
I use Symfony for the API. The API is sessionless. The tests are ApiTestCases. I alienate Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage to store the JWT token during the tests. This meant I did not have to implement a filebased cache/storage myself. With MockFileSessionStorage I have a key-value storage and I do not have to do anything more.
Using a cache component (FilesystemAdapter or ArrayAdapter) was too much work for me at the time. In my opinion, the ArrayAdapter would also have to be integrated into a Singelton for it to work.
namespace App\Tests\Mock;
use Symfony\Component\HttpFoundation\Session\Session;
class MockFileSessionStorage
{
public static function getSession(): Session
{
$session = new Session(
new \Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage(
realpath(__DIR__ . '/../var')
)
);
$session->setId('key');
return $session;
}
}
In the test method in which the JWT token is in the response body after successful authentication.
// SecurityControllerTest
public function testAuthSuccessAndIsJwtTokenInResponseBody()
{
$session = MockFileSessionStorage::getSession();
$session->clear();
// stuff
$responseData = json_decode($response->getContent(), true);
$session->set('jwt_token', $responseData['token']);
$session->save();
}
This method is an abstract class that other test controllers also need.
// AbstractController
public function getJwtAuthHeader(array $options): array
{
if (false === isset($options['headers'])) {
$options['headers'] = [];
}
$session = MockFileSessionStorage::getSession();
$options['headers']['Authorization'] = sprintf('Bearer %s', $session->get('jwt_token'));
return $options;
}
And the call in UserControllerTest.
// UserControllerTest
public function testUpdateAction(): void
{
$options = [];
$options = $this->getJwtAuthHeader($options);
$response = static::createClient()->request('PUT', '/api/users', $options);
// stuff
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论