Printed on: November 28, 2024
In a earlier submit, I wrote about utilizing the #anticipate macro to make sure that sure assertions you need to make about your code are true. We checked out testing boolean situations in addition to errors.
On this submit, I would love 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 situations in your take a look at are met, and to abort your take a look at if these situations usually are not met. The important thing distinction between #anticipate and #require is that #anticipate won’t trigger a failed assertion to cease the take a look at.
#require is way stricter. If we discover one assertion to be unfaithful within the #require macro, we finish the take a look at as a result of we do not suppose it is smart 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 elective worth will be unwrapped. We’ll additionally see how you should use #require to make sure that a particular error is or will not be thrown. And naturally, we’ll additionally take a look at boolean situations within #require.
Let’s begin by Non-compulsory.
Unwrapping optionals with #require
Typically in our code we may have elective values. They’re just about unavoidable in Swift and so they’re truly a very useful gizmo. In your take a look at, it’s fairly seemingly that you will need to guarantee that a sure worth exists earlier than continuing along with your take a look at. A method to do that can be to make use of the #anticipate macro and be certain that some property or worth will not be nil.
Nonetheless, generally you may need to take your elective worth and use it as enter for one thing else otherwise you need to do additional testing with that object. In that case, it is smart to abort your take a look at totally if the elective occurs to be nil.
We will use the #require macro for this, right here’s how:
@Take a look at func userIsReturned() async throws {
let userStore = UserInfoStore()
let person = Consumer(identify: "John")
userStore.addUser(person: person)
let returnedUser = strive #require(userStore.getUser(withName: "John"), "Consumer retailer ought to return the person that was added")
#anticipate(returnedUser == person, "Consumer retailer ought to return the person 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 elective that’s returned by getUser, the macro will throw an error and so our take a look at will truly fail. That is fairly helpful while you actually do not need to proceed your take a look at if no matter you are making an attempt to require is not there.
So on this case I need to evaluate the return person with the one which I’ve tried to retailer. I can not do this if the person is not there. So I need my take a look at to not simply fail when the elective that is returned by getUser is nil, I need this take a look at case to finish.
Now let’s think about that I additionally need to finish my take a look at if the returned person and the saved person aren’t the identical…
Checking boolean situations with #require
Within the earlier part I used the next to line to guarantee that my getUser operate returned the proper person:
#anticipate(returnedUser == person, "Consumer retailer ought to return the person that was added")
Discover how I am utilizing #anticipate to check my returned person to my saved person.
This expectation will enable my take a look at to proceed operating even when the expectation fails. This might enable me to carry out a number of assertions on an object. For instance, if I have been to verify whether or not the person identify, the person’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 take a look at to fail and finish if I didn’t get the appropriate person again.
So I am evaluating the 2 customers like earlier than and I’ve changed my #anticipate with #require. This is what that appears like in a full take a look at.
@Take a look at func userIsReturned() async throws {
let userStore = UserInfoStore()
let person = Consumer(identify: "John")
userStore.addUser(person: person)
let returnedUser = strive #require(userStore.getUser(withName: "John"), "Consumer retailer ought to return the person that was added")
strive #require(returnedUser == person, "Consumer retailer ought to return the person that was added")
print("this would possibly not run if I received the improper person")
}
Discover that I needed to prefix my #require with the strive key phrase, identical to I had for getting my returned person on the road earlier than.
The rationale for that’s if I did not get the appropriate person again and it does not match with the person that I simply saved, my take a look at will throw an error and finish with a failure.
General, the APIs for #require and #anticipate are fairly comparable, with the important thing distinction being that #require wants the strive key phrase and your take a look at ends if a requirement is not met.
Now that we have seen how we are able to use this to unwrap optionals and verify boolean situations, the following step is to see how we are able to use it to verify for sure errors being thrown.
Checking errors with #require
If you know the way to verify for errors with the #anticipate macro, you mainly know easy methods 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 take a look at 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 need to duplicate all the pieces that is in there on this submit, so for an in-depth overview, you possibly can check out that submit.
On this submit, I might identical to to offer you a quick rundown of what it seems wish to verify for errors with the #require macro.
So first let’s have a look at how we are able to 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 will verify that giving an incorrect enter to an object will truly throw the error that I need 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 take a look at if the improper error was thrown, then it makes whole sense for me to make use of #require as a result of I need to abandon the take a look at as a result of there isn’t any level in persevering with on.
Just like the #anticipate macro, we are able to go a particular error (like I did within the instance above) or an error kind (like ValidationError.self). If we need to assert that no error is thrown, we may go By no means.self because the error. kind to guarantee that our operate name doesn’t throw.
Just like the #anticipate macro, you should use the #require macro to verify whether or not a sure expression throws an error primarily based on a extra difficult analysis.
For all of the completely different overloads that exist on #require, I want 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 take a look at to proceed, however it should fail with an error on the road the place your assertion failed. With #require, your take a look at case will merely finish on the road the place one thing that you simply did not anticipate truly occurred.
In Abstract
General, I fairly like that Swift testing permits us to have a unfastened checking for assertions within the #anticipate macro, the place we are able to validate that sure issues are or usually are not appropriate with out failing your complete take a look at. That might permit you to make an entire bunch of assertions and see which of them fail, fixing one drawback at a time (operating your take a look at once more, fixing the following drawback that exhibits up) is tedious.
The #require macro is very nice while you just about depend on one thing to be returned or one thing to be true earlier than you possibly can proceed.
For instance, unwrapping an elective if you wish to use no matter you are making an attempt to unwrap to run additional code and carry out additional assertions. It is unnecessary to proceed your take a look at as a result of that each single assertion that comes after it should fail, so I actually like utilizing #require for these sorts of conditions and #anticipate for those the place I can proceed my take a look at to gather extra details about the outcomes.
