Tofu¶
Tofu is a collection of test utilities, some of which are integrated to work as a plugin with pytest.
The main feature of Tofu is its ability to let you write
of test Fixture
classes, each of which contains a
collection of related objects used in a test.
Tofu also gobbled up another little test project, called tut which implemented a few utilities for testing exceptions, dealing with temporary files, etc. All this functionality is now also part of Tofu.
Test Fixtures¶
Fixture¶
- class reahl.tofu.Fixture¶
A test Fixture is a collection of objects defined and set up to be used together in a test.
A Fixture can be used by many tests and also by other Fixtures.
To create your own Fixture, create a subclasses of Fixture. On such a subclass, a new member of the Fixture is defined by a specially named method that is able to create the object.
The name of such a ‘factory method’ is new_ with the name of the object appended.
class MyFixture(Fixture): def new_name(self): return 'myname'
A Fixture is used inside a with statement or as using a plugin for a test framework.
A member of the Fixture can be accessed by referencing it by name on the fixture as if it were an attribute.
The first time a program references a member on the Fixture instance, the corresponding new_ method will be called behind the scenes (without arguments) to create the object. Subsequent accesses of the same member will always bring back the same instance which was created on the first access.
with MyFixture() as fixture: assert fixture.name is fixture.name
If the created singleton object also needs to be torn down, the new_ method should yield it (not return), and perform necessary tear down after the yield statement.
Singletons are torn down using this mechanism in reverse order of how they were created. (The last one created is torn down first.) Singleton instances are also torn down before any other tear down logic happens (because, presumably the instances are all created after all other setup occurs).
A Fixture instance can be used as a context manager. It is set up before entering the block of code it manages, and torn down upon exiting it.
Changed in version 3.2: Added support for del_ methods.
Changed in version 4.0: Changed to work with pytest instead of nosetests (
with_fixtures
,scope()
,uses()
).Changed in version 4.0: Removed .run_fixture and .context.
Changed in version 4.0: Removed _del methods in favour of allowing new_ methods to yield, then tear down.
- clear()¶
Clears all existing singleton objects
scenario¶
- reahl.tofu.scenario¶
alias of
reahl.tofu.fixture.Scenario
- class reahl.tofu.fixture.Scenario(function)¶
A Scenario is a variation on a
Fixture
.A Scenario is defined as a Fixture method which is decorated with @scenario. The Scenario method is run after setup of the Fixture, to provide some extra setup pertaining to that scenario only.
When a Fixture that contains more than one scenario is used with
with_fixtures()
, the test will be run once for every Scenario defined on the Fixture. Before each run of the Fixture, a new Fixture instance is set up, and only the current scenario method is called to provide the needed variation on the Fixture.
set_up¶
- reahl.tofu.set_up¶
alias of
reahl.tofu.fixture.SetUp
- class reahl.tofu.fixture.SetUp(function)¶
Methods on a Fixture marked as @set_up are run when the Fixture is set up.
tear_down¶
- reahl.tofu.tear_down¶
alias of
reahl.tofu.fixture.TearDown
- class reahl.tofu.fixture.TearDown(function)¶
Methods on a Fixture marked as @tear_down are run when the Fixture is torn down.
Integration with pytest¶
with_fixtures¶
- reahl.tofu.with_fixtures¶
- class reahl.tofu.pytestsupport.WithFixtureDecorator(*fixture_classes)¶
A decorator for injecting
Fixture
s into pytest test method arguments.This decorator takes a list of
Fixture
classes as arguments and ensures that the first declared positional arguments of the test_ function it decorates will be populated with instances of the correspondingFixture
classes when the test is run.The names of these positional arguments do not matter.
If a
Fixture
in this list has scenarios, the test function will be run repeatedly–once for each scenario. If more than oneFixture
in this list has scenarios, the test_ function will be repeated once for each combination of scenarios.For example:
class MyFixture(Fixture): def new_string(self): return 'this is a test' @with_fixture(MyFixture) def test_this(my_fix) assert my_fix.string == 'this is a test'
The use of
Fixture
classes can me mixed with pytest.fixture functions. In such a case, theFixture
instances are passed to the first declared positional arguments of the test function, leaving the remainder of the arguments to be interpreted by pytest itself:class MyFixture(Fixture): def new_string(self): return 'this is a test' class MyOtherFixture(Fixture): def new_int(self): return 123 @pytest.fixture def another_string(): return 'another' @with_fixture(MyFixture, MyOtherFixture) def test_this(my_fix, other, another_string) assert my_fix.string == 'this is a test' assert other.int == 123 assert another_string == 'another'
New in version 4.0.
uses¶
- reahl.tofu.uses(**fixture_classes)¶
A decorator for making one
Fixture
use others.The following will result in an instance of FixtureClass1 being instantiated every time a MyFixture is created. This instance will be available in MyFixture as its .name1 attribute:
@uses(name1=OtherFixture) class MyFixture(Fixture): def some_method(self): assert isinstance(self.name1, OtherFixture)
New in version 4.0.
scope¶
- reahl.tofu.scope(scope)¶
A decorator for setting the scope of a
Fixture
.By default, all
Fixture
s are in ‘function’ scope, meaning they are created, set up, and torn down around each test function run. With @scope this default can be changed to ‘session’ scope. A session scopedFixture
is created and set up the first time it is entered as context manager, and torn down only once: when the test process exits.If the
Fixture
contains multiple scenarios, a session scoped instance is created and set up for each scenario.@scope('session') class MyFixture(Fixture): pass
New in version 4.0.
Testing for exceptions¶
expected¶
- reahl.tofu.expected(exception, test=None)¶
Returns a context manager that can be used to check that the code in the managed context does indeed raise the given exception.
- Parameters
exception – The class of exception to expect
test – Either a function that takes a single argument or a regex. If test is a function, it will be called upon catching the expected exception, passing the exception instance as argument. A programmer can do more checks on the specific exception instance in this function, such as check its arguments. If test is a regex in a string, break if str(exception) does not match the regex.
Specifying test and regex_test are mutually exclusive.
For example, the following code will execute without letting a test break:
with expected(AssertionError): # some code here # ..... # then at some point an exception is raised raise AssertionError() #.....
Changed in version 4.0: Changed to allow a regex in test keyword argument.
NoException¶
- class reahl.tofu.NoException¶
A special exception class used with
expected()
to indicate that no exception is expected at all.For example, the following code will break a test:
with expected(NoException): # some code here # ..... # then at some point an exception is raised raise AssertionError() #.....
check_limitation¶
- reahl.tofu.check_limitation(coded_version, msg)¶
Warns that a newer Python version is now used, which may have a fix for a limitation which had to be worked around previously.
- Parameters
coded_version – The version of Python originally used to write the code being tested. The limitation is present in this version and the test will only break for newer versions than coded_version.
msg – The message to be shown if a newer Python version is used for running the code.
Temporary files and directories¶
file_with¶
- reahl.tofu.file_with(name, contents, mode='w+')¶
Creates a file with the given name and contents. The file will be deleted automatically when it is garbage collected. The file is opened after creation, ready to be read.
- Parameters
name – The full path name of the file to be created.
contents – The contents of the file. Must text unless binary mode was specified, in which case bytes should be used.
mode – The mode to open the file in, as per open() builtin.
temp_dir¶
- reahl.tofu.temp_dir()¶
Creates an
AutomaticallyDeletedDirectory
.
temp_file_name¶
- reahl.tofu.temp_file_name()¶
Returns a name that may be used for a temporary file that may be created and removed by a programmer.
temp_file_with¶
- reahl.tofu.temp_file_with(contents, name=None, mode='w+')¶
Returns an opened, named temp file with contents as supplied. If name is supplied, the file is created inside a temporary directory.
- Parameters
contents – The contents of the file. Must text unless binary mode was specified, in which case bytes should be used.
name – If given, the the name of the file (not including the file system path to it).
mode – The mode to open the file in, as per open() builtin.
AutomaticallyDeletedDirectory¶
- class reahl.tofu.AutomaticallyDeletedDirectory(name)¶
A directory that is deleted upon being garbage collected.
- Parameters
name – The full path name of the directory.
- file_with(name, contents, mode='w+')¶
Returns a file inside this directory with the given name and contents.
- temp_dir()¶
Returns a directory inside this directory.
- sub_dir(name)¶
Returns a directory inside this directory with the given name.