PHPpunit 测试随机失败

如何解决PHPpunit 测试随机失败

我有一个测验课程。 此类根据测验对象的级别和类型从数据库中加载 10 个问题: 级别 0 先加载 10 个,级别 1 加载接下来的 10 个,依此类推。

所以在我的测试中,我在测试数据库中创建了 30 个问题。 然后我创建了不同级别的测验对象,并检查测验步骤数组中的第一个问题是否符合我的预期。

此测试“quiz_contain_steps_depending_on_type_and_level()”至少每 5 次发布随机失败一次。

这是 QuizTest 类

<?php


namespace App\Tests\Quiz;

use App\Quiz\Question;
use App\Quiz\Quiz;
use App\Quiz\QuizQuestionRepositoryManager;
use App\Quiz\QuizStep;
use App\Quiz\QuizType;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectRepository;
use Faker\Factory;
use Faker\Generator;
use ReflectionException;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Config\Definition\Exception\Exception;


class QuizTest extends KernelTestCase
{
    use QuestionLoremTrait;
    use PrivatePropertyValueTestTrait;

    private Generator $faker;
    private ?EntityManagerInterface $em;
    private ObjectRepository $questionRepo;
    private QuizQuestionRepositoryManager $quizQuestionManager;

    protected function setUp(): void
    {
        $kernel = self::bootKernel();
        $this->faker = Factory::create();
        $this->em = $kernel->getContainer()->get('doctrine')->getManager();
        $this->em->getConnection()->beginTransaction();

        $this->questionRepo = $kernel->getContainer()->get('doctrine')->getRepository(Question::class);
        $this->quizQuestionManager = new QuizQuestionRepositoryManager($this->questionRepo);
    }

    protected function tearDown(): void
    {
        parent::tearDown();
        $this->em->getConnection()->rollBack();
        $this->em->close();
        $this->em = null;
    }

    /**
     * @test
     * @dataProvider provideQuizDataAndFirstQuestionExpectedIndex
     * @param array $quizData
     * @param int $firstQuestionExpectedIndex
     * @throws ReflectionException
     * @throws \Exception
     */
    public function quiz_contain_steps_depending_on_type_and_level(array $quizData,int $firstQuestionExpectedIndex)
    {
        //We have questions in db
        $questions = [];

        for ($q = 1; $q <= 30; $q++) {
            $question = $this->persistLoremQuestion($this->faker,$this->em);
            $questions[] = $question;
        }
        $this->em->flush();


        //When we create Quiz instance $quiz
        $quiz = new Quiz($this->quizQuestionManager,quizData:  $quizData);

        //When we look at this $quiz steps property
        $quizSteps = $quiz->getSteps();
        /** @var QuizStep $firstStep */
        $firstStep = $quizSteps[0];

        //We expect
        $this->assertNotEmpty($quizSteps);
        $this->assertCount(10,$quizSteps);

        //We expect if quiz is type normal and level variable questions depends of level:
        $this->assertEquals($firstStep->getQuestion(),$questions[$firstQuestionExpectedIndex]);

    }

    public function provideQuizDataAndFirstQuestionExpectedIndex(): array
    {
        return [
            [[],0],[['type' => QuizType::NORMAL,'level' => '1'],10],'level' => '2'],20]
        ];
    }
}

这是生成假问题的特质

<?php

namespace App\Tests\Quiz;

use App\Quiz\Question;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Faker\Generator;

Trait QuestionLoremTrait{

    /**
     * This function persist a aleatory generated question,you must flush after
     * @param Generator $faker
     * @param EntityManagerInterface $em
     * @return Question
     * @throws Exception
     */
    public function persistLoremQuestion(Generator $faker,EntityManagerInterface $em): Question
    {
        $nbrOfProps = random_int(2,4);
        $answerPosition = random_int(0,$nbrOfProps - 1);
        $props = [];

        for ($i = 0; $i < $nbrOfProps; $i++){
            $props[$i] = $faker->sentence ;
        }

        $question = new Question();

        $question
            ->setSharedId(random_int(1,2147483647))
            ->setInfo($faker->paragraph(3))
            ->setStatement($faker->sentence ."?")
            ->setProps($props)
            ->setAnswerPosition($answerPosition)
        ;

        $em->persist($question);

        return $question;
    }
}

这是我的测验课:

<?php


namespace App\Quiz;


use Symfony\Component\Config\Definition\Exception\Exception;

class Quiz
{
    /**
     * Quiz constructor.
     * @param QuizQuestionManagerInterface $quizQuestionManager
     * @param array $quizData
     * This array of key->value represent quiz properties.
     * Valid keys are 'step','level','type'.
     * You must use QuizType constant as type value
     * @param string $type
     * @param int $level
     * @param int $currentStep
     * @param array $steps
     */
    public function __construct(
        private QuizQuestionManagerInterface $quizQuestionManager,private string $type = QuizType::FAST,private int $level = 0,private array $quizData = [],private int $currentStep = 0,private array $steps = [])
    {

        if ($quizData != []) {
            $this->hydrate($quizData);
        }
        $this->setSteps();
    }


    private function hydrate(array $quizData)
    {
        foreach ($quizData as $key => $value) {
            $method = 'set' . ucfirst($key);

            // If the matching setter exists
            if (method_exists($this,$method) && $method != 'setQuestions') {
                // One calls the setter.
                $this->$method($value);
            }
        }
    }

    public function getCurrentStep(): int
    {
        return $this->currentStep;
    }

    public function getLevel(): int
    {
        return $this->level;
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function getSteps(): array
    {
        return $this->steps;
    }

    private function setCurrentStep($value): void
    {
        $this->currentStep = $value;
    }

    private function setLevel(int $level): void
    {
        $this->level = $level;
    }

    private function setType($type): void
    {
        if (!QuizType::exist($type)) {
            throw new Exception("This quiz type didn't exist,you must use QuizType constante to define type",400);
        }
        $this->type = $type;
    }

    private function setSteps()
    {
        $this->steps = [];
        $questions = $this->quizQuestionManager->getQuestions($this->type,$this->level);
        foreach ($questions as $question) {
            $this->steps[] = new QuizStep(question: $question);
        }
    }
}

这是问题类:

<?php


namespace App\Quiz;

use App\Repository\QuestionRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass=QuestionRepository::class)
 */
class Question
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private ?int $id;

    /**
     * @ORM\Column(type="integer")
     */
    private ?int $sharedId;

    /**
     * @ORM\Column(type="string",length=1000,nullable=true)
     * @Assert\Length(max=1000)
     */
    private ?string $info;

    /**
     * @ORM\Column(type="string",length=255,nullable=true)
     */
    private ?string $statement;

    /**
     * @ORM\Column(type="array")
     */
    private array $props = [];

    /**
     * @ORM\Column(type="integer")
     */
    private ?int $answerPosition;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getSharedId(): ?int
    {
        return $this->sharedId;
    }

    public function setSharedId(int $sharedId): self
    {
        $this->sharedId = $sharedId;

        return $this;
    }

    public function getInfo(): ?string
    {
        return $this->info;
    }

    public function setInfo(?string $info): self
    {
        $this->info = $info;

        return $this;
    }

    public function getStatement(): ?string
    {
        return $this->statement;
    }

    public function setStatement(?string $statement): self
    {
        $this->statement = $statement;

        return $this;
    }

    public function getProps(): ?array
    {
        return $this->props;
    }

    public function setProps(array $props): self
    {
        $this->props = $props;

        return $this;
    }

    public function getAnswerPosition(): ?int
    {
        return $this->answerPosition;
    }

    public function setAnswerPosition(int $answerPosition): self
    {
        $this->answerPosition = $answerPosition;

        return $this;
    }
}

如果有人理解这种行为。我提前感谢他帮助我睡得更好:-)

解决方法

感谢@AlessandroChitolina 的评论。

在我的测试中创建的问题集在我的数据库中并不总是以相同的顺序记录。

因此,我没有从我的起始 $questions 数组中测试预期的问题,而是在新的 $dbQuestions 数组中从数据库中检索问题。这解决了我的问题。

这是新的测试:

/**
     * @test
     * @dataProvider provideQuizDataAndFirstQuestionExpectedIndex
     * @param array $quizData
     * @param int $firstQuestionExpectedIndex
     * @throws \Exception
     */
    public function quiz_contain_steps_depending_on_type_and_level(array $quizData,int $firstQuestionExpectedIndex)
    {
        //We have questions in db
        $questions = [];

        for ($q = 1; $q <= 30; $q++) {
            $question = $this->persistLoremQuestion($this->faker,$this->em);
            $questions[] = $question;
        }
        $this->em->flush();

        $dbQuestions = $this->questionRepo->findAll();

        //When we create Quiz instance $quiz
        $quiz = new Quiz($this->quizQuestionManager,quizData:  $quizData);

        //When we look at this $quiz steps property
        $quizSteps = $quiz->getSteps();
        /** @var QuizStep $firstStep */
        $firstStep = $quizSteps[0];

        //We expect
        $this->assertNotEmpty($quizSteps);
        $this->assertCount(10,$quizSteps);

        //We expect if quiz is type normal and level variable questions depends of level:
        $this->assertEquals($firstStep->getQuestion(),$dbQuestions[$firstQuestionExpectedIndex]);
    }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-