Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My TicketBeast notes #97

Open
sileence opened this issue May 6, 2019 · 5 comments
Open

My TicketBeast notes #97

sileence opened this issue May 6, 2019 · 5 comments

Comments

@sileence
Copy link

sileence commented May 6, 2019

Get the ball rolling

Focus on the value you want to deliver with your application.

Work backwards ⬇️

Choose most important thing your app needs to do?

  • Purchasing tickets ✅ — Most important feature
  • Integrate with Stripe
  • Publishing concerts
  • Editing concerts
  • Adding concerts
  • Logging in as a promoter
  • Creating accounts
  • Inviting promoters

Then start with the most simple test (of the most important feature):

  • Purchasing tickets
    • View the concert listing ✅ — Easiest to implement
      • Allowing people to view published concerts
      • Not allowing people to view unpublished concerts
    • Pay for the tickets

Test structure

Tests are usually divided into 3 parts:

  • Arrange: Setup the conditions necessary to perform the test (A.K.A. given). i.e.: Create a concert.

  • Act: Run the code you want to test (A.K.A. when). i.e.: Purchase concert tickets

  • Assert: Make assertions to verify outcome (A.K.A. then) i.e.: Make sure the customer was charged the correct amount and that an order exists for that customer.

Direct model access

Your test code has direct access to the domain code in your app. This allows you to:

  • Make tests faster
  • Make design decisions about your app right into the test code
  • Remove duplication

Programming by wishful thinking

Write the code you wished it existed and then use the failing tests to implement that code.

Model Factories

Factories allow us to keep our tests focused on the things that are actually important.

  • By default, only specify the info that is absolutely necessary for the record to be created
  • Use them to avoid errors in your tests because of required columns —not relevant to the current test— not being provided
  • Factory states allow you to make your acceptance tests more expressive by getting rid of details that you should really only worried about specifying at the unit level.

Other Tips

💡Store money as cents (instead of float points).
💡Look for opportunities to make your tests faster (i.e. by not migrating or talking to the database).
💡See a test failing for a reason you'd expect it to fail is a good way to know you've got your test right.
⚠️ Think harder, don't hope tests will give you all the info you need! (or detect all possible errors).

Purchasing Concert Tickets

Browser Testing vs Endpoint Testing

Browser testing: simulate the user's actions inside the browser.

✅ Simulate how a user would interact with the application.
✅ Gives complete confidence the app is working end-to-end.
⚠️ Slower
⚠️ Brittle

The other cons mentioned in the lesson (complex to setup, can't interact with code directly) are solved by Laravel Dusk.
To make your Laravel Dusk Browser tests less brittle use dusk-* attributes instead of relying in CSS selectors.

Endpoint testing: simulates how the browser would interact with the server (instead of how the user would interact with our app), by making HTTP requests directly to an endpoint.

✅ Faster
✅ Interact with more stable data structures (won't break if aesthetic changes are made to the UI)
✅ Interacts directly with the code
⚠️ Untested gap between frontend and backend

What do I want from my tests:

  • Confidence: that the system works
  • Reliable: don't break for unimportant reasons
  • Speed: so they are executed often
  • Simple: few tools as possible, environment easy to recreate.

How to start

Start with a simple test with a name similar to the class name, then add more details as you gain more insight of what you actually want to test.

Later on you can rename the first test method to make it more specific.

“One of the biggest benefits of working with a Test Driven Approach is that the tests give you a good place to play with the code and figure out exactly what you want to land in terms of design.“

Asserting Exceptions

Asserting exceptions with try/catch && fail instead of @expectedException gives you the opportunity to execute more assertions on the exception object itself.

Reduce duplication in your tests

To remove duplication in different test methods you can overwrite the setUp method to store common values as properties, replace bindings in the Laravel Container, etc. For example:

protected $paymentGateway;

protected function setUp()
{
    parent::setUp(); // <- important to preserve the setup performed by the parent classes.

    $this->paymentGateway = new FakePaymentGateway;
    // Additional common code here...
}

You can also create custom assertions or test helpers, for example:

    private function orderTickets($concert, $params)
    {
        $this->json('POST', "/concerts/{$concert->id}/orders", $params);
    }

    // Note: check the method (assertSessionHasErrors) available in the current versions of Laravel.
    private function assertValidationError($field)
    {
        $this->assertResponseStatus(422);
        $this->assertArrayHasKey($field, $this->decodeResponseJson());
    }

Additional Tips

💡 Make the tests folder match the folder structure of your application
💡 Hide Eloquent details from the controllers (Extract to new methods and unit test those methods).

Limiting Ticket Sales

💡 Include an assertion in your arrange test in order to verify that a precondition is met.

$order = $concert->orderTickets('[email protected]', 5);
$this->assertSame(5, $concert->ticketsRemaining());

💡 When you explicitly update a column in another model, example:

$ticket->update([‘order_id’ => null])

You can use that opportunity to create an explicit and more readable method in that model, example:

$ticket->release().

You might also want to unit test that method.

💡 Sometimes you might want to add redundant test coverage in order to get more explicit error messages when the tests fail. However as a downside the test suite might be slower and this might increase the work to maintain the tests suite.

@jassehomar
Copy link
Collaborator

jassehomar commented May 6, 2019 via email

@cbaconnier
Copy link
Collaborator

Please, do not comment a thumb up, but add a reaction on @sileence comment instead.
Subscribers like me don't want to get notified every time

@fwartner
Copy link
Collaborator

fwartner commented May 6, 2019 via email

@sileence
Copy link
Author

sileence commented May 6, 2019

Sorry about that guys, I deleted my comment and since there's enough interest and activity here I will just continue editing the main text to add the rest of the notes as I go through the course!

@partounian
Copy link
Collaborator

partounian commented Feb 10, 2020

Adding my notes onto this :)

Steps for creating functionality are:

(notes/details for steps are located below the steps)

  1. Bullet point wanted [high-level] features

  2. Create a Feature Test file for testing the first step(/feature?) needed for this feature (app/Test/Feature/ViewConcertListingTest.php)

  3. Write desired api (wishful programming) and run test to make sure it fails (optionally begin by adding the comments // Arrange // Act // Assert to make the test steps clear)

  4. With minimal code necessary, make the test pass [fixing it one error at a time]

  5. Create Unit Test (file)

  6. Write desired api (wishful programming) and run test to make sure it fails (optionally begin by adding the comments // Arrange // Act // Assert to make the test steps clear)

  7. With minimal code necessary, make the test pass [fixing it one error at a time]

  8. Refactor


Details for above steps

  1. Bullet point wanted [high-level] features
  • Example features: Creating Concerts, Editing Concerts, Inviting [Concert] Promoters, Purchase Tickets

  • Cross out features until you get to a single feature that is most important to your user (Purchase Tickets) [in which they direct interact with]

    • (optionally note your alternative solutions to these features that you could work with that doesn't involve writing code, i.e. use DB GUI tool, Stripe website)
  • Break down the feature into steps (1. View concert listing, 2. Pay for the tickets 3. View tickets/order confirmation in browser 3. Receive conf email with link to online order conf)

  1. Create a Feature Test file for testing the first step(/feature?) needed for this feature (app/Test/Feature/ViewConcertListingTest.php)
  • Feature test files test a single feature and thus are named by a feature available to users such as ViewOrderTest.php, PurchaseTicketsTest.php, InvitePromoterTest.php, or AcceptInvitationTest.php.
    • Feature tests should not use a browser (i.e. Dusk).
  1. Create Unit Test (file)
  • Unit tests should not test a Controller or a route. They should test various methods of a class and thus are named {$className}Test.php.

Notes to self:

  • bullet point 1 doesn't fit in so nicely as that's more like when you start a project or your day, but not the title is wording such that it assumes you already know your functionality.
  • note that each test should ideally only include information directly relevant to test itself
  • note not to worry about details such as published or unpublished concerts until you make it work with the bare minimum

WIP and I would appreciate any constructive criticism. :)

More useful resources/notes

Adam Wathan - Chasing "Perfect" (removing conditions and improving clarity)
https://www.youtube.com/watch?v=5DVDewOReoY

Pt. 2 of above
https://adamwathan.me/2015/09/03/pushing-polymorphism-to-the-database/

Refactoring with Collections & Null Objects (spiritual part 3)
https://adamwathan.me/2016/05/19/refactoring-with-collections-and-null-objects/

Livewire
https://www.youtube.com/watch?v=bllMmnCtgbk
https://www.youtube.com/watch?v=y3TQq534dRM

Eloquent subqueries
https://www.youtube.com/watch?v=IBUXXErAtuk

Adam Wathan API Drift
https://adamwathan.me/2016/02/01/preventing-api-drift-with-contract-tests/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants