Can pytest_sessionfinish still call the fixture with session as scope? - pytest

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!

Related

Pytest Scoping Issue with Fixture Indirect

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.

Using pytest patch decorator to test ray actors remote function

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()

As I am defining classes in scala, The controls is then not going inside the main function. What to do?

class User(private var name : String, var age : Int);
object Demo{
def main(args: Array[String]) {
//var user = new User("Harsh", 24);
if(true) {
println("here")
} else {
println("In else")
}
println("Ok")
}
}
This is my scala code and when I comment out the class "User" defined the output is
here
Ok
And when I am using the class, I am not getting any output.
Any idea what is happening
On JVM everything starts in main function and program will together end with main function unless there are other non-daemon threads running.
Until you get to threads just assume that if there is no call path from main to some code - then this code is never called.
If I understood your question correctly: when you uncomment the two commented lines, it doesn't compile because there's no printName method in User. If it doesn't compile, there is nothing to run so no output from running. But you should get some output telling you about the compilation error or about an attempt to run a non-existent file.

ScalaTest: assert blocking statement

I'm using some code with blocking statement:
blocking {
Thread.sleep(10*1000)
}
Is there a way to assert that this blocking statement is given? Or in other words: Can I write a test that fails if somebody removes the blocking statement?
Update: How to assert blocking when used in Futures?
Try playing with BlockContext.
You should get something like this:
var blocked = false // flag to detect blocking
val oldContext = BlockContext.current
val myContext = new BlockContext {
override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = {
blocked = true
oldContext.blockOn(thunk)
}
}
BlockContext.withBlockContext(myContext) {
blocking {} // block (or not) here
}
assert(blocked) // verify that blocking happened
Update on making it work if you want to test code wrapped in Future (comment follow-up)
When you construct the Future it's factory method takes block of code (function) to execute explicitly and execution context implicitly (commonly scala.concurrent.ExecutionContext.Implicits.global).
The block of code later will be scheduled to execution context and will be runned in one of it's threads.
Now, if you simply wrap blocking piece of code into Future inside code block passed to BlockContext.withBlockContext, like you suggest in comment:
BlockContext.withBlockContext(myContext) {
Future {
blocking { Thread.sleep(100) }
}
}
... this will not work since your current thread will only do Future construction and actual code passed to Future will be executed in thread from relevant execution context (BlockContext.withBlockContext detects blockings in current thread).
Having that said, I can suggest you to do one of 3 things:
Do not wrap code you want to test into Future. If you want to test whether piece of code uses blocking or not - just do that.
Write a function and test it, you can pass it to Future in production.
Lets assume that for some reason you can't avoid creating Future in your test. In this case you'll have to tamper with execution context that is used when constucting future.
This code sample demonstrates how one could do that (reuse blocked and myContext from my original example):
// execution context that submits everything that is passed to it to global execution context
// it also wraps any work submited to it into block context that records blocks
implicit val ec = new ExecutionContext {
override def execute(runnable: Runnable): Unit = {
ExecutionContext.Implicits.global execute new Runnable {
override def run(): Unit = {
BlockContext.withBlockContext(myContext) {
runnable.run()
}
}
}
}
override def reportFailure(t: Throwable): Unit = {
ExecutionContext.Implicits.global.reportFailure(t)
}
}
// this future will use execution context defined above
val f = Future {
blocking {} // block (or not) here
}
Await.ready(f, scala.concurrent.duration.Duration.Inf)
assert(blocked)
If your Future gets created indirectly, for example, as a result calling some other function that you run in your test, then you'll have to somehow (possibly using dependency injection) drag your mocked execution context into wherever Future gets created and use it there to consruct it.
As you can see, the first option is the simpliest one and I suggest sticking to it if you can.

Scala testing Mailer.sendEmailClientSuspended

I have the following bit of code below and need to test the email is sent when a user is suspended.
def suspendClient(client: Client, event: Event): EventResult = {
Logger.debug(String.format(s"Found Client[${client.getName}]"));
subService.suspend(client)
Mailer.sendEmailClientSuspended(client)
WebHookEventDAO.completeEvent(event.getId)
EventResult.ok
}
The main bit of logic i'm trying to test is Mailer.sendEmailClientSuspended(client) is invoked with the correct args e.g the correct Client is passed. Is it worth splitting it up into a seperate function and how difficult is it to test a 'Object' in Scala since Mailer is an Object.
Assuming you're writing your tests in Scala with MockitoSugar and ScalaTest you want to be using an ArgumentCaptor from the Mockito library. This allows you to capture the value of the client object passed as the parameter to the fundtion sendEmailClientSuspended(client).
See this stackoverflow post for a worked example you can follow. You'll need to specify the package your Client class is in, so something like this...
val capturedClient = ArgumentCaptor.forClass(classOf[com.acme.app.Client])
If your Mailer object doesn't extent a Trait currently, add one so you can mock the Trait and call verify() on it.
If you don't own the code to Mailer, you can move the call out into it's own helper class that you write, and you can then mock the new Trait. Something like this...
trait MailerTrait {
def sendEmailClientSuspended(client: Client): Unit
}
object MyMailer extends MailerTrait () {
def sendEmailClientSuspended(client: Client): Unit = {
Mailer.sendEmailClientSuspended(client)
}
}