Skipping tests conditionally in Pest
In this blog post, I'd like to show the easiest way to skip tests conditionally in tests. Using the simple technique, which can be used for other things besides skipping tests, you can make your tests much more flexible.
Exploring our use case
I'm currently working on adding AI solutions to Flare. When this feature is complete, Flare users will get AI-powered solutions out of the box with their Flare subscription. Here are a couple of examples.
Here, the AI suggested adding some code to retry a call when a deadlock occurs.
In this example, the AI detects that we make a wrong method call, and it suggest the right call from the package. It also includes a useful links to the docs.
Please note that this layout isn't final yet. When this feature is finished, we'll add a warning that an AI solution might not be 100% correct. Nevertheless, the results that we're seeing now are impressive.
Adding tests
For an integration with an external service, in this case, Open AI, I tend to write two types of tests.
The first type of test ensures that our code that surrounds the call with the external service works. In these tests, we don't call the actual API of the external system, but we use a fake. Things that are tested this way are:
- do we call the external service at the right time
- do we send the correct parameters to the external service
- do we process the response of the external service correctly
As mentioned above, for these tests, we use a fake. The actual service isn't called.
In the second type of test, we do call the external service. In these tests, we want to ensure that the code that performs the API call to the external service works correctly.
Here is the test that makes sure that we called open AI correctly. If Open AI can't respond with 10 to the simple 5+5 question, we'll assume we did something wrong.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
});
In the test above, we do make an API call to Open AI using the API key defined in config('services.open_ai.api_key')
. That config key is being filled by an .env
variable.
When I push this test, and a colleague pulls it in and runs it, that test fails on my colleague's machine. Why does it fail, you ask? Well, because .env
isn't in version control, so services.open_ai.api_key
is not set.
Instead of failing, it's better to let skip the test with a clear message that it is skipped because the necessary API key is not set.
Here's a first attempt at that.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
})->skip(config('services.open_ai.api_key') === null);
If you try to run this test, it will fail with this error message.
ERROR Target class [config] does not exist.
That might not be what you were expecting. This error is caused by PHP evaluating the expression passed to skip
before Laravel is booted up. The config
function will reach into the IoC container, which isn't built up at that point.
We simply need to wrap it in a closure to delay the evaluation of the expression passed to skip
to a moment when Laravel is booted up.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
})->skip(fn() => config('services.open_ai.api_key') === null);
With this change, the test will pass, but only on a machine where config('services.open_ai.api_key')
is set.
Let's now see what happens running the tests on a machine where config('services.open_ai.api_key')
is not set.
The output states that the test was skipped, which is good, but it doesn't specify why. Luckily, you can pass the reason as a second argument.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
})->skip(
fn() => config('services.open_ai.api_key') === null,
'Skipping test because config key `services.open_ai.api_key` is not set'
);
When running the test now, the reason why the test is skipped is displayed.
That's nice! But I don't like that the arguments passed to skip
are rather bulky. Imagine you want to skip some other tests based on config values being set. It's pretty verbose to keep repeating this code.
Pest to the rescue
Fortunately, Pest has got our back. A lesser-known feature is that you can tack on any function you want after a test, so you're not limited to skip()
, or todo()
.
The above test could be refactored to this one, where we use a function called skipIfConfigKeyNotSet
.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
})->skipIfConfigKeyNotSet('services.open_ai.api_key');
Of course, we should also define that skipIfConfigKeyNotSet
. You could define the function anywhere, but a typical place would be the Pest.php
file. Inside that function, you can use test()
to call anything you'd call in a regular test.
function skipIfConfigNotSet(string $key)
{
if(config($key) == null) {
test()->markTestSkipped("Config key {$key} not set");
}
}
With this in place, we now have a convenient way to skip a test if a config variable is not set: we can tack on skipIfConfigKeyNotSet
on any tests where it's needed.
Using our package
You can go one step further. At Spatie, we've created a package called spatie/pest-expectations containing custom expectations and test helper functions used across our projects. By packaging these up, we don't have to define these functions in all projects individually.
With the package installed, you can use the whenConfig
function (the equivalent of skipIfConfigNotSet
above).
Here's how you can use it.
it('it can get a response from open ai', function () {
$response = app(OpenAiClient::class)
->useApiKey(config('services.open_ai.api_key'))
->ask('How much is 5 + 5?');
expect($response)->toContain('10');
})->whenConfig('services.open_ai.api_key');
The package offers these similar functions that can be tacked on to any test:
-
whenEnvVar($envVarName)
: only run the test when the given environment variable is set -
whenWindows
: the test will be skipped unless running on Windows -
whenMac
: the test will be skipped unless running on macOS -
whenLinux
: the test will be skipped unless running on Linux -
whenGitHubActions()
: the test will be skipped unless running on GitHub Actions -
skipOnGitHubActions()
: the test will be skipped when running on GitHub Actions -
whenPhpVersion($version)
: the test will be skipped unless running on the given PHP version, or higher. You can pass a version like8
or8.1
.
In closing
I hope you enjoyed learning about this small test optimization. Hat tip to the amazing Luke Downing, who pointed me to the fact that you can tack on any function after a test.
If you want to know more about testing with Pest, check out our premium video course Testing Laravel, recently updated for Pest v2. Happy testing!
While skipping evaluations can be a practical way of voluntarily eliminating specific requirements from your test suite, it can also lead to avoided tests being neglected or ignored UK essay writers.
This article provides a helpful guide on how to conditionally skip tests in Pest. It's great to see the practical examples and the step-by-step explanation, making it easy to understand and implement. The use of custom functions and the Pest package for test optimization is particularly insightful. Additionally, the inclusion of the rice purity test could further enrich the testing experience. Overall, a valuable read for anyone looking to enhance their testing practices.
I have complete faith that you could contribute something of value to the discussion. I can only hear you wailing about something that you might be able to rectify if you weren't so preoccupied with seeking attention. Ultimately, I am certain that I selected to read. quordle today
When I attempt to display the PDF using Preview, FineReader, or NitroPDF (PDFPen geometry dash subzero), it is rendered void on Ventura. However, older versions of MacOS and Windows computers exhibit the file in proper format.
Your post is so interesting. Thanks for sharing this one! masonry veneer home
Very astute observations! You have shed light on this complicated matter with your analysis. moto x3m
great post! flappy bird
A trip to phrazle the mountains in the fall wouldn’t be complete without stopping in Ellijay, affectionately known as Georgia’s Apple Capital.
A tremendous amount of effort was invested. The perusal of the document was really captivating. Reading this book was a pleasurable and enjoyable experience. I am enthusiastic about continuing to read this essay, which I have bookmarked for future consultation. In order to continue and achieve success, it is crucial to consistently maintain a high level of performance. play gacha life game
In these cases, you may provide a boolean value as the first argument to the skip() method. This test will only be skipped if the boolean value evaluates to true fireboy and watergirl.
If skipping the tests does not cause negative effects on bob the robber results then we apply.
That sounds like a great idea! Conditional skipping of tests can definitely make testing more cookie clicker 2 flexible and efficient.
Skipping tests conditionally in Pest is the best for us to find the best services that provide us with the right ideas that are bringing us what we need to know. Also by using the tree removal services we can find the services that are bringing the right results that we need.
It half body sexdoll likely provides resources, such as practical strategies, exercises, or frameworks, to help readers implement this new mindset in their daily lives. These resources are crucial for making the abstract concept of mindset change actionable and relevant.
Download Bharat's Largest social gaming app to play 100+ Mobile Games & win cash prize upto 25 crores daily. 100% safe payments, quick withdrawal.
visit: [url=https://winzoapp.download]https://winzoapp.download[/url]
Here we are dedicated to providing top-tier Brazilian Jiu Jitsu gears. Bjj Rash guards
The problem you are fall guys mentioning is very useful in the present and has a future orientation.
thanks
Everything is rather honest and clear; the problems are described in great depth. There is no doubt that knowing this is helpful. Regarding sales, is my website really very profitable? Please check the following: run 3
Thanks to these codes I have completed my wordle nyt website, from the bottom of my heart I have to thank you to send it to you and surely those who need it will be like me.