Hello ~ I’m Milo!

Welcome to pay attention to my public number test development pit goods, exchange together! Like collection attention, do not get lost.

Unittest

Unittest you should be familiar with. As a unit testing framework that bloggers used most often 5-6 years ago, pyTest, Nose is slowly eating away at it.

Gradually, we saw more discussion content from UnitTest +HTMLTestRunner to Pytest +allure2 and other upstarts.

Can not help feeling, after all, he is behind The Times, unable to keep up with the tide of The Times.

Back to the topic

Finished with emotion, back to the text. Although UnitTest is slowly being abandoned, it is still a very comprehensive testing framework.

Today, I saw the tomato roll king’s remarks in the group, which aroused my memory.

I knew that unitTest was executed not in the order in which the test methods were written, but in lexicographical order. But unfortunately I was opportunistic to solve the problem (more on that later).

Let’s look at the order in which the unitTest method is executed.

The source code first look

A look at the source code (unittest.testloader) shows that when a test method under a class is loaded, the native Loader sorts and sorts the list of test methods according to the functools.cmp_to_key method.

We know that UnitTest does not require us to specify the corresponding method. In other words, unitTest automatically obtains our method from the class and specifies that any method starting with test will be considered as a test method.

Check about the self. SortTestMethodsUsing (this is a sort of way).

You can see that the comparison method is explicitly written: if x < y returns -1, x = y returns 0, and x > y returns 1.

You may not know that strings in Python can be compared, but lexicographical order must be explained here. Let’s take a look at this example:

a = "abc"
b = "abcd"
c = "abce"
print(a > b)
print(b > c)
Copy the code

If you take A guess at the result, it’s obvious that lexicographical comparisons are made in a-Z order, so if the prefix is the same but the length is different, then the one with the longer length is the one with the lower lexicographical order.

The process of finding cases in UnitTest can be simplified as follows:

  • Find the test method starting with test under the corresponding class

  • Put them in lexicographical order

  • Executed in sequence

    So it’s easy to explain why sometimes we don’t write cases in the order we want them to.

Back to the essence of the problem

Figure out why the use cases are messy and come up with solutions. Since modifying the source code is not appropriate, we have two strategies to achieve this goal.

Let’s say I have multiple test methods:

class Testcase(unittest.TestCase) :

    def setUp(self) - >None:
        pass

    def test_1(self) :
        print("Execute the first.")

    def test_2(self) :
        print("The second")

    def test_3(self) :
        print("The third")

    def test_10(self) :
        print("The fourth")

    def test_11(self) :
        print("The fifth")

    def tearDown(self) - >None:
        pass
        
if __name__ == "__main__":
    unittest.main()
Copy the code

When you do it, in lexicographical order, it’s actually 1, 10, 11, 2, 3.

Write the test method in lexicographical order

We can manually change the name of the test method, which is what I did earlier. That is, order the dictionary of cases that you want to execute first:

class Testcase(unittest.TestCase) :

    def setUp(self) - >None:
        pass

    def test_0_1(self) :
        print("Execute the first.")

    def test_0_2(self) :
        print("The second")

    def test_0_3(self) :
        print("The third")

    def test_1_0(self) :
        print("The fourth")

    def test_1_1(self) :
        print("The fifth")

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

We can do this by breaking the number into digits, and then filling in 10 zeros for the units digit. If we were to write 100 cases, we would need to fill in 2 zeros, such as 0_0_1. Of course, there wouldn’t be too many cases in a file.

What if you run into something like test_login, which doesn’t end with numbers?

In fact, it is the same, can be written as test_ number _ business pattern. The goods wrote a decoration to solve this problem, we can go to the reference.

2. Get back to the basics and solve the problem from the root

Scheme 1 uses a variety of decoration, good, but changed the name of the method itself, we can actually start with his sorting way, according to the order we write case sorting test method, can achieve the desired purpose.

Some ideas:

  1. Write a loader from the TestLoader class, rewrite the sorting method inside
  2. This new loader is passed in while UnitTest is running

Take a look at the complete code, the comments are very complete.

import unittest


class MyTestLoader(unittest.TestLoader) :
    def getTestCaseNames(self, testcase_class) :
        Call the parent class's get test method function
        test_names = super().getTestCaseNames(testcase_class)
        Get the test method list
        testcase_methods = list(testcase_class.__dict__.keys())
        Order testCase_methods by index of list
        test_names.sort(key=testcase_methods.index)
        Return the test method name
        return test_names


class Testcase(unittest.TestCase) :

    def setUp(self) - >None:
        pass

    def test_1(self) :
        print("Execute the first.")

    def test_2(self) :
        print("The second")

    def test_3(self) :
        print("The third")

    def test_10(self) :
        print("The fourth")

    def test_11(self) :
        print("The fifth")

    def tearDown(self) - >None:
        pass


if __name__ == "__main__":
    unittest.main(testLoader=MyTestLoader())

Copy the code

The execution is still wrong, is there something wrong?

Because PyCharm has a default unitTest debugging method, we need to change it to a normal method.

Try using the console: