 So my name is Prathamesh. That's my Twitter handle. Last time I checked my Twitter followers at another conference They were 9.99, so you can follow me. But I'm not sure how much the count is right now. So we'll see I'm director at Big binary. We work on Rails Ruby react react.js react native a lot of things So if you want to talk about any of these catch me later Yes, and as she said I came from I stay in Pune, but I didn't come from India I actually flew from San Francisco because I was there You should definitely come to Pune. We have good food good weather. We also have a RubyConf in August So if you want to visit Pune, that's a good excuse I'm part of Rails issues team. I have very big responsibility as part of the issues team member. I can Close issues if you open them I can close them I don't do it all the time but sometimes I do close the issues So if I have close your issue, I'm really sorry about it, but I have to do it sometimes. I Also have some warnings. So there this talk is also a bit technical non-technical. That's fine But no durians and durian jokes are allowed during the next two days at red dot ruby con So all the durian jokes are already over if you have those in your slides, please remove them. I Have another sad news for you And I'm just telling it upfront in at the start of my talk because I really don't want to disappoint you I cannot give you any stickers. Sorry. I Have seen a lot of speakers doing this like they say that okay I will give you stickers come and find me after my talk, but I'm really sorry. I don't have them That doesn't mean I like that. I don't like them. In fact, this is my laptop and I like stickers a lot. So When I started seeing tweets about red dot ruby con five was really excited because I saw that we are getting a lot of stickers I already got them in the morning, but I want more. So I want these. I also want these So if you have stickers come and find me after my talk. I'm wearing this Kodria's t-shirt and Let's talk and give me your stickers. So I want them and I'm fine with anything. I like t-shirts, too I like all swag Okay, see on a serious note. I'm not going to talk about our spec I'm going to talk about the default rails test recipe that comes bundled with rails Rails 5 is very big release and there are a lot of changes related to how we should test our apps, too so I'm going to talk about some of those changes and I promise there are no jokes after this everything is very serious We will be talking about some serious testing stuff and secrets and everything. So yeah, let's start let me tell you the biggest secret about testing rails 5 apps and Just delete your tests. That's the biggest secret once you delete your test You don't have to worry about anything. The talk is over. So Thanks, everyone. If you have any questions, I can take them I'm just kidding. Don't worry. I'm not going to stop like that. I will be speaking for next half an hour So now literally all jokes are over. Coming back to the actual content now. So Testing is very important part of our daily life. We write tests, run tests all the time And rails 5 has some interesting changes for both running as well as writing tests When we run tests, we expect that we will get good feedback about the tests Because it's an iterative process. It's not like that We write code for the first two days of the weekend, run tests on Wednesday after lunch at 1 p.m We do it all the time. So So the output that we get from running the test is very important but sadly The default way of running tests in rails was not that great before rails 5 We run all the tests using rake tests. We got some other rake tasks also to run specific tests like controllers, models and other things But we miss out on a lot of things that are present in other libraries like we cannot rerun the failed snippets We cannot run tests only by specifying the line number of the test We cannot have options like fail fast differ output In fact, we even have to include a gem to get colored output I think that should be the fundamental right of every tester, right? We should get colored output But that is also not possible by default so and Most importantly, it's not like all of these things are not possible. A lot of gems are already providing these On top of mini test, which is the library that rails test suite is built on so and mini test is Very pluggable so you can extend it using writing your own plugins and a lot of people are already doing it So maxi test it provides all of these features like failing test rerun and everything There is also m which we can use to run test only by line But they were not present in rails. So for rails 5 rails team decided that enough is enough. Let's make running tests great again So DH has also said that he would like all of these features to to be baked inside rails itself rather than Using individual gems. So rails 5 will ship with a proper test runner Instead of running our test through rake test We will now have to run them through this test runner and we can use it by running this bin rails test command And it's accepts proper argument. So it is a proper executable which can accept like whatever options we want to pass And it provides most of these missing features like rerunning snippets running only fails test Getting colored output and everything. So we get These kind of messages when we run the test and we can reuse those lines to rerun only failed fail tests And we can also provide some more options like fail fast before output backtrace and most of these things which were not present earlier They are now supported This test runner is based on mini test because mini test 5 has plug-in architecture and rails now has its own plug-in and a custom reporter which provides all of these options and Obviously all of these features were already present in a lot of libraries. So it's it's like nothing is new here It's just that it's now part of the default rail stack So there are a lot of inspirations like aspect mini test reporters and so on But the most important thing is now the default omakase stack it supports These features by default. So once you create a new brand new rails 5 app or you upgrade to rails 5 You have all of these by default in your test suite. You don't have to do like you don't have to install any other gem It just works by default So this was all about running the test now we will talk about writing tests So there are some significant changes in the way we write test in rails 5 So rails tests have typically these three components like you write unit test for your models or your simple Ruby classes Then you have functional test which test the controllers and Integration tests are testing the scenarios like complete scenarios of a particular feature They're not like there is no change related to unit tests So that stays as it is but functional and integration tests have changed a lot In fact controller tests are the only ones that are most affected by rails 5 So they do deserve a special mention and we will talk about them in detail So it's interesting actually what we test in a controller test So they generally controller actions handle incoming requests in a rails app They do some processing with that request and returns returns the response Or sometimes they redirect whatever but they return some response. So When we want to test a controller action, we should test the response that comes out of that request So this is the typical controller test case that we have in the rails for here We are sending a request post request for creating the article. We are passing some params We are checking whether the response got redirected to a particular page But we are also testing some changes that that are happening with database with this action. So We are not really testing only the response. We are also testing the database changes Which are not part of the response. So we are also changing some third party things Which are not part of the response So we are actually testing the current state. We are not testing the response But we are testing the changes in the state that happened due to that request and that state can be anything it can be controllers HTTP Response or it can be where we got redirected or it can be HTML response or JSON response It can be changes to a database also Now one of the good things about these controller test is they're very easy to write We can just write three four lines and we can test whether that controller action works properly or not We can test response. We can test database changes. We have all the helpers for them We can also test things like which template got rendered. We can test which instance variables We are using in that particular action. It's very convenient to write and run controller tests but These controller tests they actually lie. They don't actually Follow the same way that Rails follows when a typical request hits a Rails app So they bypass that typical request response cycle when these controller tests are run Because when we run these kind of functional style controller test There is no middleware stack that gets invoked when this test is run So it just directly hits that particular action and tries to see what is happening inside that action So even if we are writing too many controller tests, they're not actually following the Request response cycle that Rails actually uses when it serves request in production So even if controller tests are very easy to write They're not really testing your controller actions in true sense. They are just like unit testing your controller actions And that's where integration tests come So that's the alternative to controller tests in like functional style controller tests in Rails We can write integration test because they don't bypass the Middleware stack they follow the same structure that a typical request follows when it hits the Rails app and This is the simple integration test From Rails 4 so we can do almost the same things that we can do with a functional style controller test We can send the request using the same request help us we can send params We can test whether what happened to the response whether it got redirected or anything other than that We can also test the changes to a database similar to how we test them in functional style tests So these integration tests they are almost same as controller tests We can do the same things that we were able to do in controller tests The advantage is they're close to the real world. So they simulate the same process that happens when Request actually comes and hits our app. So It's good to write Integration tests rather than writing functional style tests, but then why don't we write it? Why don't we encourage writing integration tests? Because the reason was before Rails 5 they were really slow compared to the functional style tests. They were very slow and So that's why the recommended way was to write functional style tests when you want to test your controller actions But there was a lot of work done during the development of Rails 5 and Now the integration tests are not that slow. In fact, they're as fast as your functional style tests in Rails 5 So there is no way so there is no need to actually have the old functional style test anymore We can just write integration tests and they will work in the same way And that's why integration controller tests are now default in Rails 5 so if you generate a new Rails app and Generate a resource using scaffold or you will get a test like this instead of functional now It is using integration style tests. So these will be generated by default now onwards in Rails 5 and And the old style functional controller tests inheriting from action controller test case They're not gone completely, but they're soft-deprecated So that code is still present in the role Rails repository and you will be still able to use those kind of tests in 5.0 But Rails by default will not generate them Rails will generate integration tests So they will still continue to work if you have your older tests And you're upgrading an older app from 4 to 5 and if you have such tests, they will still continue to work This code base related to action controller test case might be removed in future versions of Rails So it can be put in into a separate gem And we will have to include that gem if we still have those tests in some other major versions of Rails So even if I said that Integration tests are almost same. You can do same thing that you are able to do in controller tests There are some implications which we need to talk about Because things are not exactly at same as functional style tests with integration tests. So With Rails 4 it is possible to access session directly in our setup or in any Any other test also so we can do something like this We can access the session set some value and then we can access that in our test Basically, we can assert against that What we are really doing here is we are sort of bypassing hitting the actual endpoint which hits that this session value And we are actually testing the second step after hitting after assigning the session But this will not work in Rails 5 So you cannot use like you cannot use session in the same way that you are allowed to use in Rails 4 So if you try to do this same with Rails 5, we will get a error like undefined method session for nil class Because in Rails 5 we have to actually send a post request for that particular URL Which will set the session in in the controller code. So instead of us setting the session directly through tests We have to set we have to introduce another level which will directly hit that URL set the session And then we can test what we wanted to test We can also like extract. This is a very common pattern and we'll most probably need it for all of our tests So we can also extract it into a helper method. So if you are using device You might be used to writing like sign-in ads and then the username that actually sets the session internally directly In the controller test, but that will not work in the Rails 5 So if you are used to writing feature tests, you might be logging in for the every request like In setup block login for every request and then test what you wanted to test So we have to follow the same way now for integration tests in Rails 5 Same is same case with things like headers also So earlier it was possible to directly set headers on request and other stuff. That is also not possible now You have to send those headers separately as part of parts of the argument to that request So because of this change of switching to integration tests as default our functional style controller tests are no longer functional style controller tests So all the rules of writing an integration test now apply to writing a controller test And these integration tests are like more black box style tests rather than our functional tests So we are not actually allowed or recommended to test the internals of our functional style tests We have to test what comes out of them. So it is like more black box testing than what we had before and This brings us to the interesting question like what now we cannot test in a controller test that it has changed the integration test What we cannot do that we were allowed to do before so The recommended way is as it is more black box testing don't go inside the controller and try to test more and more Controller internals now. What are the controller internals? Let's see one of the example so We can have something like this We have some instance variable in our control reactions and in Rails 4 it was allowed It was allowed that you can test which instance variable was assigned using this assigned helper You can test what template got rendered. So this all was possible in Rails 4 But is it really testing the thing that we wanted to test we are just verifying that this instance variable was assigned But we are not testing the value. We are testing this template got rendered But not actually testing what actually got rendered from that template. So we are just cracking the surface We are not actually testing the things that the controller is doing So the same test can be rewritten in a Rails 5 style integration test and We we are we are actually asserting against the actual template here Like what actually got generated through this HTTP request So we are select like we are checking the DOM and seeing like this particular element got rendered And then this can match our database value So if I have a repo with this particular name then I can test my DOM that okay This response got generated and this that response has this particular test now You must be worried like why I'm doing these kind of things. I'm coupling my test with the generated HTML or generated JSON so Actually, that's what the integration tests are trying to recommend They're trying to recommend that test the response that got generated from the request rather than testing this hypothetical things like Say like instance variables or template directly test the response that got generated from the request So this gem Rails DOM testing gem it provides a lot of helpers for asserting facts against the generated HTML and I just show an example of assert select But there are a lot of other things like CSS select and you can have your custom Counts for how many elements you want and stuff like that. So this gem can be a useful thing for writing controller testing Rails 5 So what we are actually trying to do is we are not really testing the controller particular controller action in isolation We are testing the controller and view together. So it's like you're Treating the controller and view interface as the same thing So we are more and more focusing on end-to-end testing What is getting generated from that particular request and we are asserting against what got out of the response And all of these things like assigns assert template all of these helpers They were actually testing the view interface like what got passed from my controller to my view, but Now we are treating it as a black box. So everything is as a black controller and you are together We are not allowed to or recommended to go inside and test what is happening inside And we are just testing what is coming outside of that black box. So We are no longer testing the controller view interface that we were testing in Rails 4 So the controller view interface is just like an implementation detail Rails has some way of passing data from controllers to views We don't have to actually test how it is passing or what it is passing. We just we just test what comes out of it And that's why these helpers assert template assigned. They're all deprecated in the Rails 5 So if you are using these helpers, you will get a warning in the Rails 5. You cannot run those steps directly But another question why would why we were doing it in the first place? Why we were testing such things like instance variables or which template got rendered? Why we are testing the controller view interface? Because sometimes it's useful to test that controller view interface in isolation Let's say you're building a Rails engine and you have a separate app which is consuming that engine So in that case you your controller is coming from the engine, but views are present in your Rails app So in that case it's important to actually see that your controller is specifying a particular contract And it is providing certain instance variables to your views So in that case it can make sense to actually test the controller view interface So if you are using device in that case the controllers are present in device But your views are present in your Rails app So we know that these instance variables are available from our device controllers, which we can use in our views So in such cases it's important to actually test this interface in isolation too That's why Rails has not completely removed it. So it's present in a gem Rails controller testing gem and if you are upgrading to Rails 5 from Rails 4 And if you have such test you can include this gem and thanks to Goshaang he wrote this gem so You can include this gem and you will get those helpers in your Rails 5 app So let's talk about the HTTP request methods that we use for testing our controller actions like get, post, patch delete and whatever So we use these helpers like post and we pass the parameters like I want to create a Product with name FIFA. So I'm passing the parameters as a hash and if you see we are not specifying anywhere that Product name FIFA is to be considered as params. So we are just specifying the hash directly So we are using positional arguments. The first argument that we pass to the HTTP request method is the Is the params? Rails knows it internally, but it's not the only argument that we can pass We can pass optional hash of session variables We can pass optional hash of flash values and if you notice everything is optional So all the three arguments are optional. So It it's okay when you start with writing like sending just params But it gets tricky when you start writing like you don't want params, but you want flash you want flash But you don't want session variables. So it gets really confusing like this So here in this case, I don't want name. Sorry. I don't want params, but I want flash and session variables I don't want params and session variables. I want only flash So it gets very tricky and confusing what I'm really trying to do So no idea what I'm trying to do Okay, it worked So in the Rails 5 we have a better solution now We no longer have those positional arguments because Rails 5 depends by default on Ruby 2.2.2 plus so we can use keyword arguments for such helper methods and Rails is taking advantage of keyword arguments and now if we try to pass the params directly as params without specifying anything We will get a deprecation warning that you have to pass them through keyword arguments So we can now specify like these are my params. These are my session variables These are my flash messages using keyword arguments. So it's become a bit better If your test is using action controller test case, then we have access to params flash session and Ajax request or not and if you are using integration tests, then we have access to params, environment, headers and etc So if you are using like if you generate new Rails tests in Rails 5 You cannot set session and other things because Action dispatch integration test don't allow those keyword arguments This also had some implications. Nothing is free So if you have something like this in Rails 4 where you are passing the Params as the first argument and the key of the parameters session So this used to work in Rails 4. It used to treat these params key with session but in Rails 5 this session is interpreted as session not params because session is the keyword argument Which which has some meaning in Rails 5. So if you are If you have something like this edge case, it may not work correctly in Rails 5 So that's one thing to note about these request methods Let's talk about Testing API requests. So Rails 5 you can generate now API only apps In API only apps we have JSON or XML request. We don't have HTML requests So we have to have some better way of testing those requests So by default when we write a controller test or integration test the request is treated as a HTML request if you don't specify what we want to actually do So in case of Rails 4 if you have to specify it as a JSON thing We have to specify a lot of things like we have to specify the format We have to specify the params suggestion. We have to also specify the content type as JSON So too many times we have to specify what we want to do Instead of that if there was some way that Rails can internally figure out that this is JSON request So I have to pass the params as JSON. I have to set these proper headers It will be good for us So we don't have to specify a lot of things and that's what Rails 5 does So we just have to specify the request as as JSON and internally it will figure out that okay I have to set these headers. I have to set map. I have to send my params as JSON params. So We don't have to specify everything manually. It will just work out of the box and One more good thing about this is if you're testing the response that comes out of your JSON endpoint You have to actually like convert your body to JSON before testing it But when you specify the encoder as JSON, it will figure out of converting the body to JSON format also So one more helper method can be removed from your test cases So yeah, it figures out the content type based on what you want and then it will use the pass body Like we can use the pass body method to test to write assertions So this was all about like from controller functional integration test, but there are some other changes too. So There is a now active job async adapter for our tests So by default active active job had two adapters in line and a sync but only in line was default for Writing active job test. So if you're using sidekick in production, then when you're writing test and writing test for your active job Classes, they were actually using the inline adapter So even if you're using like if by default like by any chance if you are dependent on any asynchronous behavior in production In development, you are doing things in synchronous fashion. So now rails five will have a Sync adapter which will be default for test mode. So it will be as close to your production environment as possible So it's like a better default option to have because we will not run into random issues which happen only in production So rails can speed up our test more by running tests in parallel like it can use your CPUs It can use different processes to run tests faster But for that we also have to make sure that if you if you run our test in random order They will not fail randomly sometimes that happens like your one test is dependent on your second test the order matters So rails five will also have this configuration option where the test will run in random order by default So even if the parallel thing like speeding up test using parallel test has not happened yet It's sort of a good step towards that goal that now we have our test running in random order So we can fix issues which are dependent on running tests in a particular order and we can We may fix some of the like some of the bugs also in our apps So testing in rails five is better experience a bit smarter experience with the introduction of test runner Because now we can learn test more effectively than we used to do There are a lot of things that have changed later to controller testing also But I think the biggest takeaway is that we are moving towards writing more end-to-end testing and we are not writing Many like we are not writing the old style functional test So we are more focusing on writing end-to-end tests There is much more in rails five than this week We just covered the changes later to testing today But if you want to know more about rails five we have a block series on rails five So we have around more than 50 blog posts on various features in rails five So I would highly recommend checking that There is also a newsletter this week in rails started by Godfrey who is the rails core team member So if you subscribe to this newsletter, you will get a like weekly scoop of things of what happened in rails in that particular week So I will highly recommend this too if you are interested in knowing what is happening in rails in each week And that's all I have. Thank you Thank you Wait Thank you brother mesh for your really really cool talk on test Any questions for him? We can accept one question before lunch. Okay, I get all of you hungry Anyway, take note of his Twitter handle if you want to contact him. He's also here in Singapore for Quite a few more days. So say hi. Thank you very much