I have the following:
// conftest.py
import pytest
#pytest.fixture(scope="session")
def deviceundertest(request):
return request.param
#pytest.fixture(scope="session")
def setupteardown(deviceundertest):
yield "conn"
#pytest.fixture(scope="session")
def data1(setupteardown):
return response
#pytest.fixture(scope="session")
def data2(setupteardown):
return response
// test_000.py
import pytest
#pytest.mark.parametrize("deviceundertest", ["server1", "server2"], indirect=True)
def test1(data1):
assert True
#pytest.mark.parametrize("deviceundertest", ["server3", "server4"], indirect=True)
def test2(data2):
assert True
// test_001.py
import pytest
#pytest.mark.parametrize("deviceundertest", ["server1", "server2"], indirect=True)
def test3(data1):
assert True
#pytest.mark.parametrize("deviceundertest", ["server3", "server4"], indirect=True)
def test4(data2):
assert True
When I run a --setup-plan and look at one of the fixtures Im seeing the fixtures setup and teardown is being performed more then its actual scoop (i.e session). Example below:
❯ pytest example_dir --setup-plan | grep "SETUP S deviceundertest"
SETUP S deviceundertest['server1']
SETUP S deviceundertest['server3']
SETUP S deviceundertest['server1']
SETUP S deviceundertest['server3']
SETUP S deviceundertest['server2']
SETUP S deviceundertest['server4']
SETUP S deviceundertest['server2']
SETUP S deviceundertest['server4']
Thanks,
Pytest doesn't check if the arguments are the same in multiple tests, every test is independent. The scope="session" is relevant only if the fixture is not invoked explicitly, which you are doing, so every parameter in each test will invoke the deviceundertest fixture.
You can implement a simple logic in conftest.py to use the same server instance in multiple tests
servers = {}
#pytest.fixture
def deviceundertest(request):
server = request.param
if server not in servers:
servers[server] = Server() # create your server instance
return servers[server]
You are sending different params to the root fixture through "#pytest.mark.parametrize", so it's creating a different fixture each time with the value you sent.
A fixture doesn't repeat the setup/teardown process only if it will 100% return the same value, in you case each time a different value can be menifested.
Related
I need call one fixture when all the tests are successful. I tried to check if all tests are successful in pytest_sessionfinish. I also put my fixture with the session scope #pytest.fixture(scope="session"). However, the code in pytest_sessionfinish can not call this fixture at all. Are there some way to solve this issue?
#pytest.fixture(scope="session")
def myfunction(
ser: Serial, logger: Logger
) -> bool:
print("In my function")
...
#pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
print("Prepare to call myfunction")
myfunction
It only showes "Prepare to call myfunction" as the output.
Thanks!
I'm trying to run unit test for a ray remote function. I am using a #patch decorator to patch the remote function. The
foo.py
class Foo(object):
def __init__(self):
self.value = 0
def bar(self):
self.value = 100
print("In original method")
assert False
test_foo.py
from unittest.mock import patch
import pytest
import unittest
import ray
from tests.foo import Foo
#pytest.fixture
def ray_fixture():
print("Initializing ray")
if not ray.is_initialized():
ray.init()
yield None
print("Terminating ray")
ray.shutdown()
def fake_bar(self):
print("In fake method")
assert True
#pytest.mark.usefixtures("ray_fixture")
class FooTestCase(unittest.TestCase):
"""Test cases for Foo module"""
#patch("foo.Foo.bar", new=fake_bar)
def test_bar(self):
Foo().bar()
#patch("foo.Foo.bar", new=fake_bar)
def test_bar_remote(self):
foo_actor = ray.remote(Foo).remote()
obj_ref = foo_actor.bar.remote()
ray.get(obj_ref)
The test test_bar passes and test_bar_remote fails.
If I use ray.init(local_mode=True) then both tests pass. I can not use local_mode=True due to other limitations.
How can we patch ray actor's remote method using #patch?
Here's an alternative. Subclass Foo with a stubbed/mocked implementation and use it in ray. That way, the Foo class would be intact, you would only update those that needs to be mocked e.g. the method bar().
test_foo.py
...
class FooStub(Foo):
def bar(self, *args, **kwargs):
print("In another fake method")
assert True
# Optionally, you can also call the real method if you want. You may update the arguments as needed.
# super().bar(*args, **kwargs)
#pytest.mark.usefixtures("ray_fixture")
class FooTestCase(unittest.TestCase):
...
def test_bar_remote(self):
foo_actor = ray.remote(FooStub).remote()
obj_ref = foo_actor.bar.remote()
ray.get(obj_ref)
...
Output
$ pytest -q -rP
..
================================================================================================= PASSES ==================================================================================================
__________________________________________________________________________________________ FooTestCase.test_bar ___________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout setup ------------------------------------------------------------------------------------------
Initializing ray
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
In fake method
---------------------------------------------------------------------------------------- Captured stdout teardown -----------------------------------------------------------------------------------------
Terminating ray
_______________________________________________________________________________________ FooTestCase.test_bar_remote _______________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout setup ------------------------------------------------------------------------------------------
Initializing ray
---------------------------------------------------------------------------------------- Captured stdout teardown -----------------------------------------------------------------------------------------
Terminating ray
2 passed, 1 warning in 5.03s
I have found a below hacky way which involves change in original function (and check env variable to provide test implementation)
import os
class Foo(object):
def __init__(self):
self.value = 0
def bar(self):
self.value = 100
if os.environ.get['TEST'] == 'True':
print("In fake method")
assert True
else:
print("In original method")
assert False
runtime_env = {"env_vars": {"TEST": "True"}}
ray.remote(Foo) .options(runtime_env=runtime_env) .remote()
I had a method in mock service,
def whenDynamoDBActionBlacklist(newlist: List[String]) = {
doReturn(newlist).when(service).Blacklist
}
and want to test it by using
val list = mocks.whenDynamoDBActionBlacklist(List("333:avd"))
but I can't get the value, got the nullPointerException, can anyone help me with that? Thanks.
I believe you are confusing the difference between mock setup and mock execution. whenDynamoDBActionBlacklist does not actually return a value, instead in just specifies responses for the stubbed methods. Consider the following example
import org.mockito.{ArgumentMatchersSugar, IdiomaticMockito}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class MockitoScalaExampleSpec extends AnyFlatSpec with Matchers with IdiomaticMockito with ArgumentMatchersSugar {
"MockitoScalaExample" should "demonstrated difference between mock setup and mock execution" in {
// system under test
trait MyService {
def blacklist: List[String]
}
// initialise mock of the system under test
val serviceMock = mock[MyService]
// specify behaviour of the mock by defining how should stubbed methods respond
serviceMock.blacklist returns List("mocked!")
// actually execute the mocked system under test
val result = serviceMock.blacklist
// assert on the result
result should be(List("mocked!"))
}
}
The key is to understand
serviceMock.blacklist returns List("mocked!")
does not yet return List("mocked!") but it declares what should happen once we call aMock.blacklist.
Note the example uses mockito-scala but the same concepts apply in vanilla Mockito.
I have a test module has one autouse fixture
import pytest
#pytest.fixture(autouse=True):
def set_env_config(monkeypatch):
palladium_config = os.path.join(os.path.dirname(os.path.dirname(os.getcwd())), 'config.py')
monkeypatch.setenv('PALLADIUM_CONFIG', palladium_config)
from A import B
and in every followed test within this test module class B is needed, but this importation can not be achieved for any tests.
In the other way, I patch only the environment variable only
#pytest.fixture(autouse=True):
def set_env_config(monkeypatch):
palladium_config = os.path.join(os.path.dirname(os.path.dirname(os.getcwd())), 'config.py')
monkeypatch.setenv('PALLADIUM_CONFIG', palladium_config)
and import the class B in every test case, it succeeded.
Why is that ? why can't I import class within autouse fixture
thanks a lot
I am guessing that you are expecting something like the following:
#pytest.fixture(autouse=True)
def do_an_import():
from A import B
def test_foo():
assert B.do_my_thing() == 'foo'
That doesn't work, while doing the following does what you want:
def test_foo():
from A import B
assert B.do_my_thing() == 'foo'
Unfortunately doing an import in the fixture (the first example) will add B into that fixture function's namespace, but not the test function's namespace.
Similarly, this wouldn't work for the same reason:
#pytest.fixture
def not_an_autouse_fixture():
from A import B
def test_foo(not_an_autouse_fixture):
assert B.do_my_thing() == 'foo'
B is being imported into the fixture's namespace, which is different from the test's namespace. You could instead do:
#pytest.fixture
def Cls():
from A import B
return B
def test_foo(Cls):
assert Cls.do_my_thing() == 'foo'
or you could import it at the top level of your module like:
from A import B
def test_foo(B):
assert B.do_my_thing() == 'foo'
I have the following piece of code:
import java.util.concurrent.Executor
import scala.concurrent.Future
trait Storage {
def store(location: String, content: Array[Byte])(implicit exec: Executor): Future[String]
}
object LocalStorage extends Storage {
override def store(location: String, content: Array[Byte])(implicit exec: Executor): Future[String] =
Future {
... do some stuff ...
"Hello"
}
}
And here comes the code for testing:
object LocalStorageTest extends App{
import scala.concurrent.ExecutionContext.Implicits.global
val testImage = AsyncWebClient get "some url"
val result = testImage.flatMap{content => LocalStorage.store("logo.png",content)}
val status =Await.result(result, 30 seconds)
println(status)
AsyncWebClient.shutDown
}
Whenever I try to run the code, I am getting the following error:
Cannot find an implicit ExecutionContext.
Why? Isn't scala.concurrent.ExecutionContext.Implicits.global in scope already? When this import is added directly to LocalStorage object, it works (!)
BUT I am loosing a possibility to alter the context. Since I do not want to run this code on global every time. As this is a part of an akka application and for "production" runtime it is supposed to run on some dispatcher. Just for testing I would like to run it on a global execution context.
Am I missing some important concept here or is my design wrong that I am loosing such flexibility? Where is the issue?!
An ExecutionContext is not the same thing as an Executor. You should make the method accept an implicit exec: ExecutionContext (scala.concurrent.ExecutionContext) and then it will behave as you expect.