Phpunit: how to mock a user session

I have a class called CustomerController that uses injected IUserSession and IGroupManager objects for determining if the calling user is member of some predefined group. Depending on the outcome of the test, other class methods may return different results. The class constructor looks like this:

	public function __construct(IRequest $request,
								IUserSession $userSession,
								IGroupManager $groupManager,
								CustomerService $service) {
		parent::__construct(Application::APP_ID, $request);
		$this->service = $service;
		$this->userSession = $userSession;
		$this->groupManager = $groupManager;
		$uid = $this->userSession->getUser()->getUID();
		$this->isadmin = $this->groupManager->isInGroup($uid, 'some-group');
	}

Now, in order to unit test the class, I need to set up an environment that provides functionality of IUserSession and IGroupManager. I went the straightforward way in my CustomerControllerTest class:

	public function setUp(): void {
		parent::setUp();
		$this->request = $this->getMockBuilder(IRequest::class)->getMock();
		$this->userSession = $this->getMockBuilder(IUserSession::class)->getMock();
		$this->groupManager = $this->getMockBuilder(IGroupManager::class)->getMock();
		$this->customerService = $this->getMockBuilder(CustomerService::class)
			->disableOriginalConstructor()
			->getMock();
		$this->customerController = new CustomerController($this->request, $this->userSession, $this->groupManager, $this->customerService);
	}

This does not work as the mock userSession will not return an IUser object on which getUID() can be called, so the class under test fails with an error.

How do I provide the right user session environment for testing?

The user executing the test will not be a valid NC user, so I need to mock something. AFAIK it is not possible to override the return method of a mock object, so I cannot just overload everything. I shall also want to run tests on different outcomes of the group test, that is, first tests where the user is a member of said group and second tests where he is not.

Maybe I should change CustomerController so that the two statements are bundled in a method that I can overload with a mock? Again the return value limitation strikes…

It is. Use something like

$user = $this->createMock(IUser::class);
$this->userSession->expects(self::once()
    ->method('getUser')
    ->willReturn($user);

Hey, I wanted to ask if PHPUnit tests are run “with a user”?

If I run tests which call classes that use DI to inject string $userId into their constructors, the tests crash because it seems like the container doesn’t hold a value for $userId, which leads to passing null into the class constructor.

So, is it possible to have the tests run “with a logged-in user”, or alternatively mock the user so that all constructors that require string $userId are called with a value other than null?