Testing The Way It Should Be (aka Intro Into Cypress)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so hello everyone I'm Brian man I am the creator of Cyprus and today we're gonna be talking a little bit about testing the way that it should be now for me Cyprus has consumed every aspect of my life for nearly two-and-a-half years now but you've likely never even heard of it so in that case your first question might be what is Cyprus well Cyprus is a testing tool that's written entirely in JavaScript that helps developers like you write automated tests for the web it's the type of testing tool that drives a browser like a real user would and it's most often compared to selenium although it is substantially different than selenium now Cyprus is an incredibly visual tool and I've found for all of us to get on the same page it's easiest to just show it in action so let's go through a quick sixty second demo so here we are in the Cypress desktop app booting our project we've launched Chrome and we're now testing apples main navigation well now searching through apples support system applying some filters clicking through a help document and finally testing it in responsive mode with Trello we're now logging in we are creating a new board we're adding a story adding a comment and applying a label now we're logging in again we're clicking through the Trello settings and lastly we're now testing trelles homepage and modifying the view the viewport to be responsive and with Cypress we can walk back in time through our commands so as we hover on the command Cypress root stores Dom to the state that it was in at that time we display key indicators like highlighting the elements in question or even providing the exact hitbox where an action such a such as a click took place and we can also pin these snapshots allowing this to manually inspect the Dom or potentially cycle through multiple snapshots such as before and after an action took place all right now the cynic in you may be wondering what is the world really need another testing tool and to answer that let me first share my own experiences with testing so I've been building web apps for the last nine years and in that time I've gotten a chance to lead development team and often teach developers how to test their applications but whenever our team would invest significant resources into writing integration and end tests would always inevitably run into major problems often times giving up writing those tests altogether and after seeing this fail over and over again for years it ultimately led me to conclude that testing is just too hard I mean really lit let's be honest the tools that we use were originally invented in 2004 so we've had 12 years since then but it still takes a dedicated staff of QA engineers to do well so when I decided to set out and try to fix this problem in the early days of Cypress experiments I really just focus on what I wanted to improve for my own team but what I was missing was external validation that this was really a problem for developers everywhere and for instance I wanted to know do other developers really feel the same kind of pain and while I was experimenting with the first few prototypes of Cypress I got a recommendation to read this book and I cannot express how valuable it was to the early days of development and lean startup does all that it can to prevent you from building a product that nobody wants that to build something that the world really needs you must constantly run experiments and validate your ideas with real users in other words you have to get out of the building and although I new testing as a technical practice I had no idea what other users were facing and more importantly what they thought the barriers were so I took to the Lean Startup philosophy and attempted to validate my assumptions and I asked the world of developers what are the biggest testing challenges that you want solving and the world responded developers poured in from all companies sizes and countries tell me their struggles with testing and the validation was obvious developers all over the world resonate with the same fundamental frustrations as I do and since those early days we have received thousands upon thousands of these snippets but as I'm sure you know developers complain a lot so I still wanted to understand them better armed with this data I started to organize them into distinct categories and what I found was that our three recurring themes that developers find challenging over and over again and they can be organized into the following first one is set up so every project and every developer has to initially set something up to start testing this is the first barrier this is the first barrier and it's a large one so developers struggle to even begin testing and thus the very first taste they get is a bitter one the second is writing so every project and every developer must of course write tests but what we find here is the developers want an API that is much easier to write that performs much faster and helps them figure out what in the world just happened when things don't go as planned number three management so developers struggle to even understand why a test failed right we invested resources into preventing regressions so when we see tests fail we need a clear and direct way of understanding why but what we find here is really the opposite you've written the tests but they oftentimes don't help you debug a problem so why is this the case well the main tool that underpins nearly all of this feedback is of course selenium perhaps you've heard selenium or you've heard of webdriver and you've likely used a testing tool built on top of selenium such as protractor webdriver IO Nightwatch capybara or countless others well selenium first came around in 2004 as selenium RC and then morphed into webdriver which has now been adopted as the official specification so slam clearly isn't new it's fully baked and yet wire developers clearly struggling to use it well web has dramatically evolved since the early days and I'm sure you all know that right back in the day it was common for all the applications to be simple and stateless you click the button and you've got a full page refresh you submitted a form you got another full page refresh you know Ajax wasn't even really a thing does anyone remember dotnet web forms oh those were the days but and the JavaScript code that we did right wasn't for building the application itself it was only for decorating it with some fancy form validation or teasing animations and more importantly the code that we did write was mostly synchronous that is when events would trigger like click key down focus in Mouse over we would respond to those events sync and update the Dom synchronously and for these types of applications selenium worked great when it was all very simple but nowadays everything and I literally mean everything is asynchronous there are no guarantees when anything is ever done and because of this it just wreaks havoc on the way that the selenium webdriver API works these days most of our application state is kept in the browser and it changes over a period of time so gone are the days of full page refreshes so that brings me to the most fundamental flaw in the way that webdriver works web drivers architecture is a stateless HTTP API and quite simply it is literally impossible to use a stateless API to test a stateful system like this is a computer science problem and no matter how many backflips you do there's really no way to fix it when you're issuing webdriver commands think of it as issuing remote commands into a black box or picture this it's like shooting at a firework into the night sky imagine it's pitch black outside you cannot see anything and then boom the firework goes off and for a moment you see all the shapes and figures the state of the world but then it's gone again and no matter how many fireworks that you shoot into the sky you will never know the truth you will only see one tiny snapshot one moment in time and this is why tests and selenium are flaky it cannot predict nor respond to changes in your application it can only ask a simple question what does your state look like at this arbitrary moment another problem is control so traditionally with end-to-end tests you have no real control over things like you do in a unit test for instance the browser does what it does and when you go to login or request data for a page your server really has to respond with that data and that means that data really has to be in the database you can't mock or stub things which means that you're constantly having to act like a real user and will guess what in theory this makes sense but in practice getting a robot to mimic how a human behaves is perilously difficult robots aren't real users robots have no intuition and they're really stupid the last problem is just speed oh my god selenium is so freaking slow it's prohibitively slow in the sense that there's no you could ever TDD a feature as it's being built you're basically forced to write tests after the fact once you've built everything in place okay so given these challenges hypothetically you can muster your way through them but just wait because it's about to get a lot worse so for instance chroma is automated by chromedriver which uses the debugger protocol and the debugger protocol is the same protocol that your dev tools uses to provide you feedback on everything that's happening on your page network requests javascript console output errors the debugger CSS style elements etc and guess what that means that when Chrome driver is automating your browser you cannot have dev tools open I mean what are you serious like when I'm building a web app I literally never even close dev tools it's the lifeline for anyone working on the web so how can you really right run and debug tests without access to the dev tools like how did this ever work at any rate so try to open dev tools while your tests are running and you'll be faced with this message can't do it it will fail the test and you will want to rage quit testing altogether all right so lastly when your tests do fail as they will all the time you're basically forced to debug went went what went wrong with a stack trace now it blows my mind to think that this is actually helpful browsers are enormous ly complicated pieces of software and naturally because web applications are stateful they change over a period of time so when tests do fail you're reduced to a stack trace and a terminal but how does this help us see insight into what the root cause was I mean perhaps something happened several commands before this one perhaps a network request failed or maybe something just took a bit longer than you expected I mean it's tough it's really really tough and so at the end of the day you might be feeling a bit like this that I hate testing but just keep in mind this is an incredibly difficult problem to solve and there are those of us who are working to make the entire process better and I for one believe that it can be done and that there are reasonable solutions to these problems so my first epiphany to kind of fix the testing ecosystem was to simply ditch selenium all together and rebuild the architecture from the ground up to really work for testing modern applications so let's take a look at some of the key differences of how cyprus addresses that developers biggest concerns with testing so remember developers say that they struggle just to get their initial testing harness together developers feel that they spend too long picking the right tools getting them to work together and dealing with all the configuration so one of our girl aw one of our goals are early on was to create a simple streamlined onboarding process that enabled developers to write their first passing test in under five minutes in other words see success right out of the gate without having to configure or install anything other than Cypress itself so we try to make this process as easy as possible so here we're installing the cypress CLI tool from NPM and now are installing Cypress itself once that's done we write cypress open and now we're inside of the desktop application to get your project set up we just add it to Cypress and click on the road to boot the Cypress server now Cypress will automatically detect that this is a new project it will seed your project with a support directory and example fix your file and an example spec file from our kitchen sink app so when we click the spec file Cypress then launches chrome it begins running the tests so our kitchen sink app simply runs through every single Cypress command so you can just see it in action without writing a single line of code so there it is okay so once you're set up the vast majority of your time naturally is spent writing new tests so this is the area that we have focused on the most we still have much more to do but we'll highlight some of the biggest and most important features that exist today the first thing you'll notice is that as Cypress runs its commands you receive visual feedback for each individual command you issue to Cypress and as commands are being processed they'll look like they're visually pending and Cypress also logs out other important page events such as when xhr requests are made or when the page loads when the URL is changed when a form is submitted or when an assertion is made so without even looking at any code you can likely understand what is happening here now naturally as you're writing tests you're likely spending a lot of your time in read dealing with a failing test so another core goal of ours is to make errors obvious and clear in fact we've written no joke hundreds of custom errors so errors are oftentimes dead ends in software which leads to a lot of developer frustration so we try to make it easy for you to not only understand why something failed but understand the root cause in the situation of complex error messages we oftentimes provide an external link which provides a clearer explanation of why ciphers error adhere so usually these documents offer potential workarounds or suggest different approaches so every cypress command also has this really nifty feature that we call snapshotting so this is where we take a snapshot of the Dom when the command executed so when you click on a command in the UI a couple things happen we pin the snapshot which enables you to inspect the state of your application but we also output helpful debugging information related to the command in your dev tools console so in this case we have logged the output for the find command you can see the selector we used which was hash tag name the element that this was applied to which which was the element yielded from our previous command and then the new Dom element that this find command returned in this case one element was returned but had there been zero or more than one we would visually indicate that to you on the command itself you'll also notice that our application looks really small right here this is something Cypress does it automatically scales the application application isn't aware of this but basically it completely insulates you from different resolutions and different screen sizes it will always act the same no matter where it runs so that's why dev tools are open and it looks really small ok so here's another example of some useful debugging information in this case we clicked on the side op type command and we are nice people and we like to do nice things for you like provide a key events table of every single event that was fired during this type so we capture data such as the character code whether the key down key press text input input or key or key up event was fired so this is especially helpful because it can provide insight into what your application did for instance when we typed enter which is character code 13 which I'm sure many of you know the key down event was prevented default because react to did that internally in this n-b-c example alright so let's move away from the UI components of Cyprus and into the API or the DSL that you would code to drive the browser Cypress is built on top of mocha chai and sign-on in addition to that we've replicated api's that you're likely already familiar with such as Jacob right we've implemented the entire sizzle engine which enables you to query for elements using any kind of CSS selector the same way that you would do it in jQuery we use a fluent style API to where all the commands chain off of each other it's important to note that every command is asynchronous so it returns a promise under the hood now one of the more innovative aspects of Cypress is how it handles assertions and this is what makes ciphers so resilient to test Flake so in Cyprus you simply add assertions that describe the state that you want your application to be in and Cyprus will automatically block and retry until your application matches that state so in this example on line four we're query rating for an element with an ID of box right and then we're adding multiple assertions to it we're saying that it should have the class animate it should contain the text content after transition and it should have a width of 100 pixels and a height of 400 pixels and as you might be able to guess this is actually testing an animation and a JavaScript set timeout so let's watch Cyprus run this test if you look closely you can kind of even visually see and kind of predict how Cyprus works under the hood it knows that the element that we're sorting on is that box div it knows that we've added four assertions so it actually does not resolve until all of them associated to that element pass though our application code is not only using a random set timeout to change the text but it's also using CSS animations and but regardless Cyprus you know handles this just fine so normally when you attempt to interact with elements like when clicking Cyprus will run a series of tests on that element to ensure that the element would be accessible to a real user for instance it will automatically ensure that the element isn't being covered by another element and it will wait until it's no longer being covered it will ensure that the element is visible and if not it'll wait until it is or tell you the exact reason why it thought it wasn't visible but also make sure the element isn't disabled they'll wait for it to become enabled or timeout and tell you that the element never became enabled and lastly it will also wait for an element to stop animating and for the most part these situations work like a charm but cypher still offers you the developer a way to opt out of these kinds of checks and to force events to happen even if it's not technically possible so that means that you can avoid having to act like a real user for instance if you if you have a CSS style on a button that displays it only when your mouse is hovering over it well that's kind of a pain to test for and instead you may just wish to force the click event to happen regardless of whether or not the element is hidden and for these situations and many others this is often times just the least path of resistance so you simply tell snipers to force this event to happen easy so force true acts like an escape hatch it also comes in handy when there's something in Cyprus that we didn't consider like this example here so here's one of our early adopters was attempting to click the white anchor text here and Cyprus was erroring out complaining that it thought this anchor text was being that they're trying to click was being covered by its parent which is a TD cell right now you can see in this picture that's not the case like the text is clearly visible but what's happened here is that when Cyprus interacts with an element by default it fires the event at the exact center coordinates although you can change this in this situation the exact center just happened to be right smack in between the text in fact if it was a single pixel higher it would have clipped a text and it would have worked just fine and so this is what I mean about robots being really stupid and having no intuition right a real user if you just told them to click this they would likely know to just kind of move their mouse over it enough to hit the text or when they see the little cursor pointer appear in the browser right and really in webdriver this would actually be a much bigger problem but in Cyprus this is a perfect example where you just force true the click event and then just move on with your test right and by the way like the reason that this is happening is actually completely legitimate in this case the anchor had a display inline style attached to it and in that situation the element does not displace the space between it's text so literally if you were to put your mouse on that exact coordinate where we calculated the quick the click should happen it would miss the element and so that's why ciphers kept thinking of being covered because when we calculate the element at that coordinates we were returned the TD and not the expected anchor but welcome to a day in the life of building tools for browsers all right so continuing on with this concept due to the fundamental architectural differences of Cyprus it's possible to do all kinds of things that would otherwise be impossible to do with webdriver so in Cyprus you have native access to everything your spec code executes in the exact same run loop as your application code so that means that you can just throw down debuggers and they work the same way as in your application code in fact due to this level of control you can start to think about writing Cypress tests not as true and end tests more like a hybrid combination of both unit and integration tests so like in this example we can tap into sitout visits on before load callback and this means that you can run arbitrary JavaScript before any of your application code is run so this could enable us for instance set up like a global user object which your application could check for or potentially set some local storage value right so you can just force your application to think a current user is logged in then what we're going for here is total and absolute control of your app you no longer have to act like a user just act like a developer and create the state that you desire in your application and test just that so here's another example so imagine you have a method on your app object called login now normally your login method might do some crazy oauth logic like opening up some facebook window which is problematic because that relies on another service and their UI but in this example just you would just do what you'd normally do in a unit test right you'd stub out this function and just force it to give what you want and this way we bypass facebook altogether so one oh if you want to write a test that simulates what happens when Facebook fails well it'd be easy to just make that happen so speaking of control in Cypress you could also do things like xhr stubbing so instead of seeding your database and dealing with setup and teardown it's oftentimes just much easier to just force your server to respond with the data that you care about and this is all baked first class into Cypress and there's just so much power at your fingertips to do this so in this example we are saying that when a post request matches the the URL proposals to respond with a 500 status and no response body and in Cyprus we enable you to reference the relationships between multiple commands so on line 16 were actually telling Cyprus to wait for an F for an xhr request that matched the route that we define above so for instance if an xhr didn't go out instead of receiving an error about like a missing Dom element and wondering what the root cause of that was you would instead receive an error about the true cause of the problem that an xhr that you expected to be sent was never actually sent out so you can have a much tighter contract between your test code in the application code and in fact it we can wait on xhrs even if we don't stub them which is another excellent strategy to guard Cyprus and block its command execution until the desired state of your application has been reached so why are we stubbing the service response in the first place well simple our application is built to toss up a error modal whenever the xhr fails so here's that application in action right so notice that the modal appears briefly because as part of this test we basically dismiss at the moment that it appears if you look closely you can see that our command GUI displays these relationships between commands we have a routing table which includes a list of everything that you've stubbed and then when exit chars are made from your app we list them out so and if they match an alias that you've defined we like visually indicate that to you like highlighted in yellow so hopefully these examples give you a brief overview of some of the ways in which writing tests in Cyprus is very different than any other tool out there all right so onto part 3 management so in my position in my opinion this is actually the hardest part to really solve well and so what what exactly do I mean by test management well so as you and your teammates write tests they grow and accumulate old tests go in a maintenance mode and naturally whenever you check a new code all the tests run NCI and here is where we find ourselves needing a lot more help than what we have today and for instance one of my friends Sam ciccone who at Google he tweeted this one day that perfectly sums up managing test and CI he says that every time ID bug CI the less I understand how we can possibly land and operate Rovers on other planets I love this tweet but one of the responses was even better so Fabien replied well I don't think those Rovers are running JavaScript which you know it's probably true probably true all right so it's Cyprus nearly half of our team has spent this entire year focused on this problem and here is how we think that we can solve it the currently Cyprus is built on top of electron and we've jumped through an enormous amount of Hoops to make it perform nearly identically to Chrome except with electron you have the ability to run headless Lee so when you go to run your tests in CI they'll run in this headless mode but that of course introduces like a crucial problem right if your tests are running headless Lee how the heck do you see what happened when they failed and how do you get insight into that well the way that we're solving this is basically by building a service around CI runs so when a run finishes we pull off all the build assets and host them in our cloud platform once we process them the results are then piped directly back into the desktop app so for instance given that we have these projects set up in Cyprus after we run our tests and CI the results from those tests will be made available in the desktop app and each individual project will also have a list of builds and this serves two primary purposes the first is that we'll automatically collect all the build data for a run and give you the ability to access to this data externally similar to the way that sauce labs or browser stack works let me be clear we're not a CI provider nor are we even a competitor of sauce or browser stack we essentially sit right in the middle between all of them and we'll work perfectly well so from here you can now access this build data and because we're integrated as such a fundamentally lower level than say webdriver it means that cyphers is capable of understanding everything that happened in the browser so initially you know we'll be providing things that you'd expect like the individual errors a running video of the whole build screenshots and all the logs from everything that happened under the hood and later I see us doing other things like memory or performance regression testing arguably more importantly we'll probably do things like visual regression testing which is screenshot diffing right the last thing I just want to mention here is that even this in my opinion isn't enough right so getting access to the facts after they've happened and getting insight into your run is of course extremely important but so kind of misses what we really need as developers to debug failures in CI so for instance if you're experiencing a failure in CI even if you can clearly see in a video or in a screenshot you still have to reproduce it locally so that you can iterate on the root cause and really that's where I believe CI can only truly be solved so as part of our CI platform we'll be offering you as a way to automatically connect and watch your CI runs in real time but what's more important is that you'll be able to interact with this with the CI run exactly as if it's running on your machine so you'll have your standard browser open you'll see all of your cypress commands just like the browser was running locally and you'll be able to remotely interact with that other browser that means that we can do things like automatically synchronize your app and test code with the remote cypress instance so that when a failure occurs you can drive into it you can modify your code locally and see the results in real time all while really running in your CI instance which is where the problem is happening now beyond just features though I want to share a few goals that were focused on and that are really personally important to me for us to get right and what we're trying to build will really only be possible with the adoption in the endorsement of the open source community at large right we're not trying to build an enterprise ERP system or make something that's used at like offshore QA companies right and naturally we want to be good open-source citizens and that means having Cypress be completely open-source and managing our repos and community engagement like professionals so I mean we really want to be champions of a testing process that really works for developers right we want a clear path to success and not be afraid to be opinionated about it and as part of a clear path to success I hold our documentation up to the highest standards so we've already written several hundred pages documentation and I expect that trend to continue we always try to go out of our way to not just document how to technically do something but to further explain the reasoning the why you'd want to do something in a particular way and lastly I just want to say above all right what we're trying to create is developer happiness and I know that's probably a worn out and often cliche sounding phrase but it's true you know we're trying to create an experience that is good that works with the way that we build modern applications and to do that and to get community adoption we want developers to feel happy using ciphers and we want to be part of the solution and not just another problem so I'll close with where we're headed at riders so we're actually a five-person full-time team and we're completely funded by venture capital dollars we raised a seed round at the beginning of this year and we'll be raising a series a early next year so that means that we can finally hire more awesome engineers like yourselves to work on an awesome project so please come talk to me which by the way we use react on all of our front ends including the desktop app the Skype app or urine runner now for the bad news so at the moment Cypress is like halfway open source right many of the repos that make up suckers are public but some are not but do not worry this is really only temporary we're actually in a closed invite beta and have been this way for the last year so that means sad-faced it start using Cypress you have to be invited but since I know that's not what you want to hear for today and tomorrow we've lifted those restrictions and so if you go and download Cypress from NPM you can start using it today and you'll automatically be whitelisted forever and ever so go have fun but that said this is not a general public release this is just for you all who are here today so be nice don't be posting mean things on Hacker News we're getting there just be a little patient it's still very much in beta and one last thing just remember that Cypress isn't selenium it's not bound to the same restrictions as different ones but I think overall there are many positive trade-offs you can approach testing your application in a different way than maybe are you used to you don't have to act like a user so give it a go come chat with us let us know what you think and remember to be nice thank you great Thank You ginger we're gonna have questions so we're gonna do four minutes of questions sixty four thumbs up awesome can seifer's run headless on a continuous integration server yes it takes two lines of code and your CI script to run Cypress headless Lee it's all it's all baked into it is it supporting ie um this is uh the you know one of the big million-dollar questions right now Cypress only supports chrome chromium and canary and electron but everything that we have built at Cypress is basically down the road we're gonna support those other browsers we have to basically set up drivers for them and write the automation scripts a yes or a No yeah it doesn't right now it will there's not gonna be anything prohibitive that's gonna prevent us from supporting it in the future are there any plans about ciphers supporting for support for react native ciphers we'll only forever and ever support Dom based tooling it has a Dom it can test it mobile where they can test not native it's impossible great with redux time-traveling cypress has access to everything in your application so you would just simply hook into the state and I mean and then you just integrate it you just code to it so I don't see why not good it'd be used to test web apps on real phone if it's mobile web yes already played with Cypress it's awesome pricing for projects is keeping me from using it right now when will this info beat this closed our initial plan is to release and it be free for everyone we definitely are going to eventually put up a pay wall for private projects and we have no idea what the pricing will be but it's gonna be like typical you know software as-a-service stuff not like ridiculous or anything it's gonna be priced accordingly it makes sense can its source states so you don't have to go to have to through the whole application my journey produced your log and every time you need to test features at the end of this journey absolutely so it's not quite like that but basically you can do things in Cyprus like you can just instead of your UI you can there's a command called site on request which will automatically set and get cookies under the hood it's not bound to origin restrictions and you can essentially use what Cyprus already has to bypass having to like follow all those steps of the UI so the answer is you is yes phantom j s+ castro also solves all this problems what is the advantage of Cyprus you can't see phantom Jas are you kidding me it's headless you can't see anything so with Cyprus you work in the browser all day every day building your app like there's no difference between your development environment and your test environment you just write your code and test at the exact same time it's impossible to do that with phantom and Kassar so I don't know you're energetic about his answer yeah can we set risk with electrum it's possible it's possible I mean your spectral inspect Ron uses selenium it's really good wellspect Ron is really good but I kind of fall back to the whole Dom thing it's possible do this electron and with Chrome extensions but not supported out of the box oh there's a next question can we test it is it is possible to do definitely not supported right now in the era of components and with chess snapchat testing are the usually fragile integrations are still as relevant that's a that's a wonderful question I mean I think you can absolutely make a case for the react land of tooling that definitely full-blown ete tests or integration tests or definitely lower on the meter but you know this stuff exists but over a third of all of our early adopters use it to test for yeah so it's kind of like you don't have to necessarily have full-blown end-to-end tests and but you also have to kind of keep in mind that react amongst the entire web stratosphere is a tiny tiny tiny fraction of the amount of apps out there so so yes I know so last question what browsers are supported and what plans to have to extend to the entire entirety of the currently existing browserstack I mean we just basically have to write adaptors for all the different browser drivers it's really not that difficult it's mostly just operationally challenging for us because as soon as we support another browser we have to internally run all of our tests against that and you know our early adopters just getting your test to work in chrome completely like end-to-end is such an amazingly beneficial thing like you you have such a high degree of confidence that really the tests all these other browsers you're only getting like incremental improvements you know if everything runs in chrome you're like ninety ninety-five percent confidence and all you're really getting out of doing cross browser testing is you're just catching the tiny amounts of differences between each browsers so I mean we are gonna do it it's an important question but I think personally that cross browser testing is on it's going down especially with all the modern stuff there's not a lot of differences between all the browsers anymore awesome thank you very much Thank You Ryan [Applause]
Info
Channel: Coding Tech
Views: 77,373
Rating: undefined out of 5
Keywords: qa, quality assurance, app testing, testing, software testing
Id: pJ349YntoIs
Channel Id: undefined
Length: 36min 39sec (2199 seconds)
Published: Fri Jan 12 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.