128

In unittest, I can setUp variables in a class and then the methods of this class can chose whichever variable it wants to use...

class test_class(unittest.TestCase):
    def setUp(self):        
        self.varA = 1
        self.varB = 2
        self.varC = 3
        self.modified_varA = 2

    def test_1(self):
        do_something_with_self.varA, self.varB

    def test_2(self):
        do_something_with_self_modified_varA, self.varC

So in unittest, it was easy to put bunch of tests together that could go under one class and then use many different variables (varA and varB) for different methods. In pytest, I created a fixture in conftest.py instead of a class in unittest, like this...

@pytest.fixture(scope="module")
def input1():
    varA = 1
    varB = 2
    return varA, varB

@pytest.fixture(scope="module")
def input2():
    varA = 2
    varC = 3
    return varA, varC

I feed this input1 and input2 to my functions in a different file (let's say test_this.py) for two different functions. Here are the questions based on information above...

  1. Since I can't just declare local variables in conftest.py as I can't simply import this file. Is there a better way of declaring different variables here that can be used in different functions in test_this.py ? I have five different configurations in my actual testing for these variables, defining that many different fixtures in conftest.py and use them as function argument in five different functions in test_this.py sounds painful, I would rather go back to unittest class structure, define my variables and pick and choose what I want.

  2. Should I just declare global variables in test_this.py and use them in the functions the way I want ? Seems a bit not pythonic. This variables are only used by the functions in this file.

  3. Let's say I have test_that.py and test_them.py as well. If I have some shared variables between these different files, how would I declare them ? just create a file called variables.py in the directory where all these test files are and do an import whenever I need ? This way I can keep all data in a separate.

  4. Is it my impression that pytest discourages using a class to organize your functions? Every example I read online, it all seem to employ bunch of functions with fixtures only. What is a configuration of defining class and methods and organize tests in pytest?

  5. I have a test scenario where I have to use result of one function into another. With pytest, I have an assert that is at the end of a function not a return so I won't be able to use this function as a fixture. How do I accomplish this? I know this is not a good practice that my one test relies on another but is there a work around?

user
  • 5,370
  • 8
  • 47
  • 75
LuckyStarr
  • 1,465
  • 2
  • 12
  • 14

2 Answers2

78

1) First of all, you can declare those fixtures not only in conftest.py, but in every Python module you want. And you can import that module. Also you can use fixtures in the same way as you used setUp method:

@pytest.fixture(scope='class')
def input(request):
    request.cls.varA = 1
    request.cls.varB = 2
    request.cls.varC = 3
    request.cls.modified_varA = 2

@pytest.usefixtures('input')
class TestClass:
    def test_1(self):
        do_something_with_self.varA, self.varB

    def test_2(self):
        do_something_with_self_modified_varA, self.varC

or you can define separate variables in separate fixtures:

def fixture_a():
    return varA

def fixture_b():
    return varB

def fixture_c():
    return varC

def fixture_mod_A():
    return modified_varA

or make one fixture which returns all the variables (why not?) or even make indirect parametrized fixture which returns variables by your choice (quite confusing way):

@pytest.fixture()
def parametrized_input(request):
   vars = {'varA': 1, 'varB': 2, 'varC': 3}
   var_names = request.param
   return (vars[var_name] for var_name in var_names)

@pytest.mark.parametrize('parametrized_input', [('varA', 'varC')], indirect=True)
def test_1(parametrized_input)
   varA, varC = parametrized_input
   ...

Or even you can make fixture factory which will make fixtures for you on the fly. Sounds curiously when you have only 5 tests and 5 configurations of variables, but when you get hundreds of both, it can be useful.

3) Of course you can. But I recommend you not to import this file directly, but use command line option pointing what file to import. In this case you can choose another file with variables without changing your code.

4) I use classes in my tests because I migrated from nosetest. I didn't mention any problem with using classes in pytest.

5) In that case I propose you to do the following: fist make the function with desired actions:

def some_actions(a, b):
    # some actions here
    ...
    return c

then use it both in test and fixture:

def test():
    assert some_actions(1,2) == 10

@pytest.fixture()
def some_fixture():
     return some_actions(1,2)
Ender
  • 529
  • 1
  • 3
  • 14
Ilya Karpeev
  • 1,033
  • 8
  • 3
  • 8
    Thanks, this helped me a lot in migrating from unittest to py.test. One comment though: I think instead of `@pytest.usefixtures` `@pytest.mark.usefixtures` should be used. – Sven van der Burg May 18 '18 at 09:30
34

I think unittest is easier to read. For new testers, unittest is really easy. It is working out of the box. You depend on the Python implementation, but they will not change the interface coming years.

I like to organize my tests in such a way that I have maximum 1 test per file. In that case I don't depend on classes... but I import classes from every test to do stuff.

Some websites complain about colours in unittest is not possible. I think that is a joke, since my unittests create JUNIT output reports for Jenkins and others. There are great tools (even 1 file) to convert JUNIT to a website, that's not the responsibility of a test tool.

Also, some people complain you need a lot of code to start a unittest. I disagree, it takes 4 lines of code to create a unittest! But Pytest needs to know all the difficult annotations, which are not normal for a simple Python developer.

An important reasons also is that unittest will remain free. However, if you want to use pytest for some reason (bitbucket etc), there are tools to convert your tests and make the code less readable.

Have fun!

Bart Mensfort
  • 995
  • 11
  • 21
  • 45
    "An important reasons also is that unittest will remain free. However, if you want to use pytest for some reason (bitbucket etc), there are tools to convert your tests and make the code less readable." I'm not sure what you mean here, are you implying that pytest _won't_ be free? Also, what do you mean by 'free' here? The last paragraph is quite confusing to me – baxx Sep 14 '20 at 22:22
  • 8
    my only complaint about unittest is that they use custom syntax operations like `self.assertEqual(some_var, 5)` as opposed to a clean `assert some_var == 5` in pytest – ierdna Dec 03 '20 at 01:47
  • 1
    "However, if you want to use pytest for some reason (bitbucket etc)" Is pytest better integrated with bitbucket than unittest? – matthiash Mar 23 '21 at 14:07
  • 7
    The very last jab "make the code less readable" detracts from some of the other good points here. Part of the standard library is always a good reason to use a package, and unittest can be done without a lot of boilerplate, but "readable" is very subjective. – Davos May 06 '21 at 12:59
  • Robot framework on top of unittest will make your tests much more readable and easier to extend. When the Gherkin language is used correct, you will be amazed of the test quality. Find more details about this free technology at: https://robotframework.org/ Ofcourse, I use this with Bitbucket, Jira, Git and Jenkins without limitations. – Bart Mensfort Dec 20 '21 at 22:26
  • @ierdna I have not used `pytest` so they may have been able to achieve the same results with regular asserts (although I do not see how), but these "custom syntax operations" mean that if the test fails (ie. `some_var` is not 5), then the value of `some_var` is printed. You can still use `assert` statements in `unittest`, but the test results are more verbose using the `self.assertEqual` format. Does `pytest` also provide the value when using `assert`? – Kraigolas Jan 08 '22 at 19:38
  • 1
    @Kraigolas yes, pytest provides more explicit results than `assert`'s in unittest. – ierdna Jan 08 '22 at 20:41
  • 1
    unittest isn't easier to use than pytest neither for a casual developer nor for an advanced one . You need to create a class, derive it then you need to use `self` to access each of the assert methods. With pytest you just create a function, learn the `assert` keyword and you're done. – t3chb0t Mar 06 '22 at 17:52
  • 4
    -1 Why is that last comment "An important reason" - what's your reasoning? Is pytest not free - what has it got to do with bitbucket? – Jeppe May 17 '22 at 07:05
  • I guess @BartMensfort isn't interested in clarifying the last sentence about "unittest will remain free" ? What does it mean ? It's 3 years later now and Pytest is also still free. – matt freake Sep 12 '22 at 17:07
  • 1
    Thanks for all the comments. At the time of writing, I was afraid that pytest will not be free in the future, but that was wrong thinking. Both test-frameworks are free, although writing in pytest costs more time and I am running a business. – Bart Mensfort Sep 19 '22 at 12:40
  • 1
    In unittest there are many types of error but in pytest only te assert. To emphasize different types of errors and make code more readable, 'assert' is not explicit enough for me. But in pytest these can be added easily. For me, each test has a binary output 1 or 0. There is nothing in between. If a test fails once every 100 times, it is a big problem for your company. To debug, you have log files that should be copied separately in case of failed tests. For selling my product, I only want to know 100% correct or no go. – Bart Mensfort Sep 19 '22 at 12:48
  • Personally, I'd argue that unittest is less readable than pytest given that you are required to create classes and use references to the unittest module (e.g. self.assertXyz) over just writing tests as functions and using the `assert` keyword. Additionally, I think pytest fixtures are far easier to read and reason about than unittest's setUp and tearDown methods. Finally, I'm as confused as others about your implication that pytest isn't free. Pytest has always been free and I'm not aware of any plans to require payment for it. – celestialorb May 05 '23 at 19:06