Transcript from the "Integration Tests Solution" Lesson
>> Kent C. Dodds: The solution for this is remarkably similar to the registration experience. And actually normally in an application, I would not have two tests that are doing all this stuff, it's basically the same thing. If I really wanted to, maybe I'd abstract this test away and just call a function with a couple parameters to change a couple of those things.
[00:00:20] But what I probably do is just test one of them and then test the other one to make sure that at least you can get to the login screen or something like that. Cuz yeah, these are all using exact same components, they're very similar to each other. And that said, like it's not really expensive once you have the test to keep it around so that's it's sixes.
[00:00:41] Testing is just so loosy goosy. So before each one, we're gonna initialize our application, setup the state that we need, and then we'll render our app with the router so it has React router context all built in. We could start at a particular route say, oops, route is log-in.
[00:01:01] That's part of the abstraction that I showed you earlier from the client-test-utils. But I kinda have actually clicking and going to the route, make sure I have my route configuration set up properly. So, that's what I'm going with here. We wait for things to finish loading, so back in that abstraction here.
[00:01:27] Finish loading is using the wait abstraction which just waits until this no longer throws an error and so we expect the loading to be null. So once that query returns something that's null then we'll be good to go. Actually, as kind of a part of this, we could change this a little bit to the getByText.
[00:01:49] And then we could actually remove the whole expectation cuz this will actually throw an error also. And so, well actually no, that's the inverse. So, yeah I guess that's why I had to does the expect, but I just wanted to point out that all of the getBy APIs have a queryBy which is actually what they're built on top of.
[00:02:09] And that queryBy will return null whereas the getBy will throw an error if they can't find anything, okay? So once the loading is gone, then we'll click on the login and with the left click, thank you React router]. And we'll make sure that we've navigated to the other page, and then we'll generate a fake user.
[00:02:36] We don't care what the username password is, we'll get the username and password fields in the form and we'll set those values. And then we'll submit the form, but before we do we need to prepare Axios so that it's mocked in such a way to return the data that we want it to return when the post is called.
[00:02:57] And so we set that up with our fake user. We generate a token for that fake user and then return or resolve with data that has an object or that is an object with the user property that consists of the fake user and the token. And then we simulate a submit.
[00:03:16] Alternatively, and maybe better, it would be better to actually load this into the DOM and then click on the submit button. So you could do that either way. Danny at told me that this simulate thing from React DOM test-utils is as overly complicated and we probably shouldn't be using it.
[00:03:36] Old habits die hard, I guess. So yeah, you might want to just load this into the DOM and then do actual quick events on there. The only reason you actually have to do that is because the way React does event handling, it uses event delegation and attaches a single event handler for every event type on the body or on the document.
[00:04:00] And then any event will bubble up all the way through that and then it can send things off to where it's supposed to go. Event delegation was really popular back in jQuery days for performance reasons. I'm pretty sure that it's not really a problem any more, browsers are a lot faster.
[00:04:16] Preact, for example, does not do any of the delegation, it just attaches directly to those nodes. And so, eventually, maybe React will remove its event delegation or at least it will only attach things to the root node that you're rendering. That would be really great and then we wouldn't have to worry about putting things in the document.
[00:04:35] But for now that's what we have to do. Okay, so then, oops, after we submit we're gonna wait for all of the requests to finish and we finish loading, that loading goes away. And then, we ensure that the post on our axiosMock was called once. And that was called with auth-login and the fake user and then we verify that our logic to get the user in a login in state was actually worked.
[00:05:03] So, we now have the token in local storage, we're not on the login page anymore and the username display shows our fake user's username and that the logout button exists. Okay, so that's that, any questions about integration testing, UI, or React applications, yes?
>> Speaker 2: I was kinda wondering for both this test and last test, we checked that it wasn't on the login page or the registration page anymore.
[00:05:35] Is there a good way and we're just checking string for that.
>> Kent C. Dodds: Yeah, I think we could probably check that the, let's see, href, let's console log this, window.location.href and I think we need to run app login. So yeah, we could just say, 2b, expect, oops, there we go.
[00:06:07] And actually, it is only that because I've configured it to be so. If we got to adjust configuration
>> Kent C. Dodds: Maybe it's in, there it is. Test environments URL, so that's why it is what that is, but if you say 2b, then it couldn't also be login. So you could do that just as well, I wouldn't have any problem with that.
[00:06:31] That's actually better, it's more specific to what we want, any other questions or concerns? Okay so, one person asked me during the break to talk about testing a failure case. So, what if the log in fails? So, and how would you go about doing that? So, you could simulate a failed request right here with this mock implementation you say promise or reject.
[00:07:01] May be the server sends an error message or something like that, whatever it takes for that to happen. And then you have to check the error messages displayed properly or whatever's happening. So, maybe you'd still wait finish loading and then you dissent that, the error message is displayed and what not.
[00:07:25] I might do one of those and then if there are a bunch of different other edge cases like that I'd move that down closer to the unit test because otherwise, you wind up testing the same thing a lot. You're testing, loading the whole app, and finish loading, and all that stuff, all over, over and over and over again.
[00:07:43] Generally, I kinda stick with the happy path, and then for the higher level tests, and then when I get to edge cases, then I go down further as far as is reasonable.