Printed on: November 28, 2024
In a earlier submit, I wrote about utilizing the #anticipate
macro to make sure that sure assertions you wish to make about your code are true. We checked out testing boolean circumstances in addition to errors.
On this submit, I would really like to try a macro that goes hand-in-hand with #anticipate
and that’s the #require
macro.
The #require
macro is used to make sure that sure circumstances in your check are met, and to abort your check if these circumstances usually are not met. The important thing distinction between #anticipate
and #require
is that #anticipate
is not going to trigger a failed assertion to cease the check.
#require
is far stricter. If we discover one assertion to be unfaithful within the #require
macro, we finish the check as a result of we do not suppose it is sensible to check any additional.
On this submit, we’ll check out a number of functions of the #require
macro. For instance, we’ll use #require
to make sure that an non-obligatory worth will be unwrapped. We’ll additionally see how you need to use #require
to make sure that a particular error is or will not be thrown. And naturally, we’ll additionally have a look at boolean circumstances within #require
.
Let’s begin by taking a look at Non-obligatory
.
Unwrapping optionals with #require
Typically in our code we may have non-obligatory values. They’re just about unavoidable in Swift they usually’re truly a extremely great tool. In your check, it’s fairly doubtless that you will wish to make it possible for a sure worth exists earlier than continuing along with your check. A technique to do that can be to make use of the #anticipate
macro and make sure that some property or worth will not be nil
.
Nonetheless, typically you will wish to take your non-obligatory worth and use it as enter for one thing else otherwise you wish to do additional testing with that object. In that case, it is sensible to abort your check solely if the non-obligatory occurs to be nil
.
We are able to use the #require
macro for this, right here’s how:
@Take a look at func userIsReturned() async throws {
let userStore = UserInfoStore()
let consumer = Person(identify: "John")
userStore.addUser(consumer: consumer)
let returnedUser = strive #require(userStore.getUser(withName: "John"), "Person retailer ought to return the consumer that was added")
#anticipate(returnedUser == consumer, "Person retailer ought to return the consumer that was added")
}
The magic right here is on the road the place we create our let returnedUser
. We use the #require
macro and we name it with the strive
key phrase.
That is as a result of if the #require
macro fails to unwrap the non-obligatory that’s returned by getUser
, the macro will throw an error and so our check will truly fail. That is fairly helpful if you actually do not wish to proceed your check if no matter you are attempting to require is not there.
So on this case I wish to examine the return consumer with the one which I’ve tried to retailer. I can’t try this if the consumer is not there. So I would like my check to not simply fail when the non-obligatory that is returned by getUser
is nil
, I would like this check case to finish.
Now let’s think about that I additionally wish to finish my check if the returned consumer and the saved consumer aren’t the identical…
Checking boolean circumstances with #require
Within the earlier part I used the next to line to make it possible for my getUser
operate returned the proper consumer:
#anticipate(returnedUser == consumer, "Person retailer ought to return the consumer that was added")
Discover how I am utilizing #anticipate
to check my returned consumer to my saved consumer.
This expectation will enable my check to proceed working even when the expectation fails. This could enable me to carry out a number of assertions on an object. For instance, if I have been to test whether or not the consumer identify, the consumer’s ID, and a bunch of different properties match, I might use #anticipate
in order that I can carry out all assertions and see precisely which of them failed.
On this case I might need my check to fail and finish if I didn’t get the proper consumer again.
So I am evaluating the 2 customers like earlier than and I’ve changed my #anticipate
with #require
. Here is what that appears like in a full check.
@Take a look at func userIsReturned() async throws {
let userStore = UserInfoStore()
let consumer = Person(identify: "John")
userStore.addUser(consumer: consumer)
let returnedUser = strive #require(userStore.getUser(withName: "John"), "Person retailer ought to return the consumer that was added")
strive #require(returnedUser == consumer, "Person retailer ought to return the consumer that was added")
print("this may not run if I acquired the flawed consumer")
}
Discover that I needed to prefix my #require
with the strive
key phrase, similar to I had for getting my returned consumer on the road earlier than.
The explanation for that’s if I did not get the proper consumer again and it does not match with the consumer that I simply saved, my check will throw an error and finish with a failure.
Total, the APIs for #require
and #anticipate
are fairly related, with the important thing distinction being that #require
wants the strive
key phrase and your check ends if a requirement is not met.
Now that we have seen how we will use this to unwrap optionals and test boolean circumstances, the subsequent step is to see how we will use it to test for sure errors being thrown.
Checking errors with #require
If you understand how to test for errors with the #anticipate
macro, you mainly know the way to it do with the #require
macro too.
The important thing distinction being as soon as once more if a requirement will not be met your check case will cease.
If you wish to study extra about checking for errors, I urge you to try my weblog submit on the #anticipate
macro. I do not wish to duplicate every part that is in there on this submit, so for an in-depth overview, you’ll be able to check out that submit.
On this submit, I might similar to to offer you a quick rundown of what it seems prefer to test for errors with the #require
macro.
So first let’s have a look at how we will assert that sure operate throws an anticipated error with the #require
macro.
I will probably be utilizing the identical instance that I used within the earlier submit. We’ll test that giving an incorrect enter to an object will truly throw the error that I wish to obtain.
@Take a look at func errorIsThrownForIncorrectInput() async throws {
let enter = -1
strive #require(throws: ValidationError.valueTooSmall(margin: 1), "Values between 0 and 100 ought to be okay") {
strive checkInput(enter)
}
}
On this particular instance, it won’t make a ton of sense to make use of #require
over #anticipate
. Nonetheless, if I have been to have extra code after this assertion and it would not make sense to proceed my check if the flawed error was thrown, then it makes complete sense for me to make use of #require
as a result of I wish to abandon the check as a result of there isn’t any level in persevering with on.
Just like the #anticipate
macro, we will go a particular error (like I did within the instance above) or an error kind (like ValidationError.self
). If we wish to assert that no error is thrown, we may go By no means.self
because the error. kind to make it possible for our operate name doesn’t throw.
Just like the #anticipate
macro, you need to use the #require
macro to test whether or not a sure expression throws an error based mostly on a extra sophisticated analysis.
For all of the completely different overloads that exist on #require
, I wish to redirect you to the #anticipate
macro submit as a result of they’re precisely the identical for #require
and #anticipate
. The important thing distinction is what occurs when the assertion fails: #anticipate
will enable your check to proceed, however it’ll fail with an error on the road the place your assertion failed. With #require
, your check case will merely finish on the road the place one thing that you simply did not anticipate truly occurred.
In Abstract
Total, I fairly like that Swift testing permits us to have a unfastened checking for assertions within the #anticipate
macro, the place we will validate that sure issues are or usually are not appropriate with out failing your complete check. That may permit you to make a complete bunch of assertions and see which of them fail, fixing one downside at a time (working your check once more, fixing the subsequent downside that reveals up) is tedious.
The #require
macro is very nice if you just about depend on one thing to be returned or one thing to be true earlier than you’ll be able to proceed.
For instance, unwrapping an non-obligatory if you wish to use no matter you are attempting to unwrap to run additional code and carry out additional assertions. It is mindless to proceed your check as a result of you already know that each single assertion that comes after it’ll fail, so I actually like utilizing #require
for these sorts of conditions and #anticipate
for those the place I can proceed my check to gather extra details about the outcomes.