2 ms·
I write tests in units of work. For example, If I need to add a contact form to a page, I’ll create a test that checks whether some form exists and matches some
by tobyhinloopen 6y ago
I write tests in units of work. For example, If I need to add a contact form to a page, I’ll create a test that checks whether some form exists and matches some structure (names of fields) and I test that submitting the form performs some desired action. Usually I would replace the real mailer with a fake, test-specific mailer which has extra methods for keeping or inspecting sent mails. (There are libraries for this as well; it’s just an example).
Testing the “real” mailer might never be done since it would involve checking if the mail was actually sent. In these cases I want to isolate that boundary so it is invoked as late as possible so I can use fake mailers for my tests.
If the feature is mostly UI (like in this case) I usually test through the UI (using JSDOM and an HTTP client, or a virtual browser test runner like superagent).
If the feature is more complex like an API or something, I like to write a barebones integration test (does this HTTP endpoint actually invoke some injected module) and then I thoroughly test the underlying module which is HTTP agnostic or represents bare minimum of HTTP state without dependencies to a real
HTTP server or client. That way I can test the module without performing HTTP requests or mocking them.
I usually start by writing lines like:
- It returns no posts when there are no posts
- It returns no deleted posts
- After creating a post, posts index returns my post
- Editing my posts updates my post
- editing someone else’s post fails
- deleting someone else’s post fails
Once I’ve written the outline of my module like this, I convert them to empty tests and implement them one by one, first implementing a test, then changing the code to make the test succeed. Repeat until done. Usually I come up with more happy or unhappy cases while implementing - I instantly write a new blank test for later in these cases, adding it to the list of tests to write.
I like to always have separate implementations for implementing features: one module for the business logic (which is thoroughly tested) and one for connecting it with libraries or frameworks or whatever (with minimum tests, but at least one). This is a guideline and not a requirement: sometimes a dependency to a framework or library or external service is just part of the feature so no point in keeping it separate.