In the previous introduction, we have shared the introduction of Unittest. In the actual application, due to objective reasons, test cases that fail or fail need to be retried. Therefore, the existing unittest framework cannot meet the requirements, so we can modify whether it can meet the requirements. This article leads you to analyze how to rewrite?

So first of all, let’s try to find out, we can execute use cases at runtime in BSTestRunner, TextTestRunner, or main, so we can look at how these classes or methods are implemented.

BSTestRunner is called as follows

TextTestRunner’s method is,

The last call to the main method is also this function. We can look at the details by calling the function first and then looking at the actual call.

The last call is also to this function

So we need to find the methods in the inside to find the methods that are suitable for our use.

In the comments, we can see that it can be overridden in the stopTest method.
def stopTest(self, test) :        
    """Called when the given test has been run"""        
    self._restoreStdout()        
    self._mirrorOutput = False
Copy the code

So how do we rewrite this? So let’s sort out our thinking.

  • 1. Pass the retry times. By default, no retry is required
  • 2. Error in use case execution, marked as need to retry
  • 3. After the execution of this use case is completed, we determine whether to retry and whether the number of retries is sufficient
  • 4. If you need to retry, save the latest secondary test results.

So let’s start with the above ideas.

The following code

import  sys,copy
from io import StringIO as StringIO
TestResult = unittest.TestResult
class MyResult(TestResult) :
    def __init__(self, verbosity=1, trynum=0) :
        # The default count is 0
        TestResult.__init__(self)
        self.outputBuffer = StringIO()
        self.stdout0 = None
        self.stderr0 = None
        self.success_count = 0
        self.failure_count = 0
        self.error_count = 0
        self.verbosity = verbosity
        self.trynnum = trynum
        self.result = []
        self.trys=0#
        self.istry=False

    def startTest(self, test) :
        TestResult.startTest(self, test)
        self.stdout0 = sys.stdout
        self.stderr0 = sys.stderr

    def complete_output(self) :
        if self.stdout0:
            sys.stdout = self.stdout0
            sys.stderr = self.stderr0
            self.stdout0 = None
            self.stderr0 = None
        return self.outputBuffer.getvalue()

    def stopTest(self, test) :
        # Determine whether to retry
        if self.istry is True :
            If the number of times to execute is less than the number of retries, retry
            if self.trys < self.trynnum :
                Delete the last result
                reslut = self.result.pop(-1)
                # Judge the result. If it is an error, subtract the number of errors
                If it's a failure, subtract the number of failures
                if reslut[0] = =1:
                    self.failure_count -= 1
                else:
                    self.error_count -= 1
                sys.stderr.write('{}: The use case is being retried... ' .format(test.id()) +'\n')
                # Deep Copy use case
                test = copy.copy(test)
                # Retry times increased by +1
                self.trys += 1
                # test
                test(self)
            else:
                self.istry=False
                self.trys =0
        self.complete_output()

    def addSuccess(self, test) :
        If you succeed, don't try again
        self.istry = False
        self.success_count += 1
        TestResult.addSuccess(self, test)
        output = self.complete_output()
        self.result.append((0, test, output, ' '))
        if self.verbosity > 1:
            sys.stderr.write('ok ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('. ')

    def addError(self, test, err) :
        # Retry +1, error count +1
        self.istry = True
        self.error_count += 1
        TestResult.addError(self, test, err)
        _, _exc_str = self.errors[-1]
        output = self.complete_output()
        self.result.append((2, test, output, _exc_str))
        if self.verbosity > 1:
            sys.stderr.write('E ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('E')

    def addFailure(self, test, err) :
        self.istry = True
        TestResult.startTestRun(self)
        self.failure_count += 1
        TestResult.addFailure(self, test, err)
        _, _exc_str = self.failures[-1]
        output = self.complete_output()
        self.result.append((1, test, output, _exc_str))
        if self.verbosity > 1:
            sys.stderr.write('F ')
            sys.stderr.write(str(test))
            sys.stderr.write('\n')
        else:
            sys.stderr.write('F')

    def stop(self) - >None:
        pass
Copy the code

Now that we’re done, we can give it a try

if __name__ == "__main__":   
    suitone=suite()   
    rse=MyResult(trynum=3)    
    suitone.run(rse)
Copy the code

The execution result is as follows:

At present, the modification meets our requirements for re-trial cases, and the modification is completed.

The above is just a simple transformation, which satisfies the retry of failed test cases. In fact, it is very simple. When we have the requirements, we can find the code to be transformed according to our requirements.