4 ms·
Kinda wish Josh would mention how he structures his tests in his projects. Something I'm currently struggling at work is that within our code repos, the pattern
by azemetre 5y ago
Kinda wish Josh would mention how he structures his tests in his projects. Something I'm currently struggling at work is that within our code repos, the pattern that everyone seems to copy is mimic the src/ directory for the test/ directory, rather than co-locating tests along with components. This means a structure for components:
src/
components/
Button/
Button.tsx
Is just copied ad-hoc for tests, so we get this:
test/
components/
Button/
Button.test.tsx
Ideally it would be structured as so:
src/
components/
Button/
Button.tsx
Button.test.tsx
This pattern makes it extremely taxing to utilize codemods in the future to transform repos into something else. It also, in my opinion, instills thinking that testing is separate from feature development and is often treated as such. As a result, my org often forgoes proper testing or tacking it on at the very end to just fulfill the motions of the dev cycle.
The oddest part is that some staff and principle engineers are very adamant about this structure. It's just additional boilerplate that doesn't help and makes it hard to understand what components have tests (especially when you get in the weeds of not having a flat component directory, where child components have nested directories often several layers deep). I have no idea where this pattern permeates but it should be discouraged in most frontend projects.
Co-location of files should absolutely be encouraged, the alternative is just a jumble of directories that make it hard to grok what is actually happening.
IDK, just ranting now at this point.
- d357r0y3r 5y agoI think people do the "separate directory for tests" thing because test runners have, in the past, shipped with a default configuration to target a test directory, rather than match test files by suffix. Colocation of test files is the hands down winner and encourages the writing of tests. When you're making a change to a code file, you probably won't think to scour the codebase for relevant tests. If you see the test file right next to source file in your editor, you probably will.
- zelphirkalt 5y agoWhen you make a change to a code file, you should run your tests. Why else do you have them? A test failure should result in you going to fix that or, if appropriate, change assumptions of the test. I don't see how it is harder to add tests either.
- azemetre 5y agoIt's not that it's impossible to test, it's just more of "out of sight, out of mind." Couple this with a poor engineering culture in general, it's easier for me to understand how this pattern encourages very poor testing.
- Jernik 5y agoWhat about changes that don't cause test failures? A really quick one I can think of is a pure addition. Lets say you have tests for Feature A in 3 places. You add a thing to Feature A, and find 2 places where Feature A is tested, add tests to those, feel like you did your due diligence and move on. Co-located tests would fix this problem
- notapenny 5y agoIf they're using something like create-react-app, it may force that structure on them. I remember in earlier versions it was harder to configure other folders to put tests in. I prefer your ideal structure as well, if you're going to co-locate files relating to Button, do it properly, makes it much easier to find things and in this case see if there are tests for your component.
- azemetre 5y agohmm. I honestly never used CRA outside of an interview assessment and don't really remember much. Early on in my web career I was encouraged to create my own scaffolding tools, that practice just always stuck with me. Kinda curious to learn how they enforced directory structures. I know now they co-locate tests, but IIRC CRA always used jest and with jest you just set the globs you want to use in the config file. Hardly hardcoded or strictly enforced, but I could be wrong.
- notapenny 5y agoCould also be that I'm remembering it wrong, but I recall having some issues with CRA and structuring tests at some point. In any case, CRA sets up a separate src and test folder, so I guess a lot of people just think they should structure their tests that way.
- mikewhy 5y agoCRA doesn't enforce any structure, outside of "code lives in arc/ and public files go in public/" The default templates are just a single folder, with tests located adjacent to their source files. CRA configures jest to look for test files in arc/, but let's users override that. https://github.com/facebook/create-react-app/tree/main/packages/cra-template/template https://github.com/facebook/create-react-app/tree/main/packa... https://github.com/facebook/create-react-app/tree/main/packages/cra-template/template https://github.com/facebook/create-react-app/tree/main/packa...
- striking 5y agoWorkplace puts tests in a __tests__ folder next to the thing under test (e.g. `Button/Button.tsx` is tested by `Button/__tests__/Button.test.tsx`).
- azemetre 5y agoYes, I've seen this as well. I will admit I do using a __tests__ or __mocks__ folder to "separate" helper files for testing; but I think now I prefer core tests to be in the component directory entirely.
- throwaway858 5y agoThere are several good reasons to keep your test code in a completely separate directory (or "project") 1. Sometimes you want to do some experimental refactor in your application. This might break a lot of tests (cause them to not compile). But you first want to play around with the new change before committing to it and updating all your tests. If your test code is in the same project then the compiler errors will prevent you from doing this. 2. Your test code and application code usually need different dependencies. You don't want your test code to accidentally call functions from some helper library, and you don't want your application code accidentally calling functions from some test framework. If you have a shared list of dependencies and a large team then this will inevitably happen. 3. You don't want your application code to accidentally call helper functions from your test files. If you mix them in the same project then with a large team this will inevitably happen. 4. For code navigation and things like IDE "find usages", it is better to not be flooded with results from test code, in order to be able to focus on discovering how the code works. (Sometimes you do want to be taken to the test code which is why good IDEs allow you to choose to toggle on/off cross-project navigation). 5. Bonus: Sometimes you may want to write your test code in a different programming language then your application. This is uncommon but does happen. For example a C library with a test suite written in C++. Or a webapp backend with tests written in a scripting language using selenium. In these cases you have to have a separate project, and so for consistency you do it as well also for tests that use the same programming language. I think the last point is actually the most important: it helps formulate the understanding that your test suite should be viewed as its own separate and independent program, not inherently tied to the library code that you are developing. This leads to two insights: 1) there's no reason why you couldn't have more than one test suite to test your library (possibly developed by different teams). 2) more interestingly: you should be able to take your test suite, and run it against a different implementation of your library. This makes sense for something like a test suite for a filesystem or SQL database. But even for your custom library, if you ever need to do a rewrite, or port to a different platform/language, then being able to take your existing test suite with you will be invaluable.
- gherkinnn 5y agoThat's exactly what Deno does [0] in its standard library. Colocate tests with code. It just works. Some people also like to go further the bad way and group code by type rather than feature. src/ components/ componentA.tsx componentB.tsx hooks/ hookA.ts hookB.ts tests/ componentA.test.tsx componentB.test.tsx Angular pre 1.5 liked this a lot. Must be a Java thing. https://deno.land/std@0.129.0/collections https://deno.land/std@0.129.0/collections
- azemetre 5y agoI do wonder if it's Java as well, I've never been exposed to that ecosystem but it seems many bad (IMO at least) practices seem to originate from Java. Another one I despise is the page object pattern for E2E tests. Basically introducing a pseudo-DSL tied to something that is ever changing. I like the Cypress approach of using app actions instead: https://www.cypress.io/blog/2019/01/03/stop-using-page-objects-and-start-using-app-actions/ https://www.cypress.io/blog/2019/01/03/stop-using-page-objec... I utilize this in my playwright tests since I found it very useful when writing cypress tests previously.
- joshwcomeau 5y agoI agree! I'd do `Button.test.tsx` right inside the component directory. Managing a mirrored structure feels like a lot of wasted effort. Same for Storybook stories (`Button.story.tsx`). That said, I'm not 100% sold on the value of testing React components, outside of some very specific cases (eg. a very complex component with lots of internal state). I prefer to write integration / e2e tests that check views/pages/flows as a whole. And so for those, since they aren't connected to specific components, I do have a separate `/tests` folder.
- nerdyadventurer 5y agoCo-location does not matter much if you are used to JetBrains IDE shortcuts to navigate the project ex: CTR + E : Recent files and etc. Checkout talk about Mouseless Driven Development by Hadi Hariri[1] [1] https://www.youtube.com/watch?v=UH6YVv9js3s https://www.youtube.com/watch?v=UH6YVv9js3s
- azemetre 5y agoNever seen this talk, will definitely watch it later today. One thing I am reluctant about is having a project dictated by your IDE/editor. I think this is an antipattern. Not everyone uses JetBrains products, I myself use vim. It just makes me sad that some people prefer to have their linting, testing, and other automated checks handled by their IDE rather than using proper git hooks or scripting in general. I'm mostly a web developer so maybe other types of projects this makes sense. Will check out the video, always happy to be exposed to new ways of doing things!
- nerdyadventurer 5y ago> Not everyone uses JetBrains products Yes, you are right > It just makes me sad that some people prefer to have their linting, testing, and other automated checks handled by their IDE rather than using proper git hooks or scripting in general. I agree with you things like linting, testing should check in CI. there is actually benefit of co-locating whether you use JetBrains IDE or not like easier to explore the code base when you diving into existing project.