CarryFront
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Topic 41 - 42 ###### tags: `The Pragmatic Programmer` ## Topic 41 Test to Code > 🧠 Testing Is Not About Finding Bugs We believe that the major benefits of testing happen when you ==think about and write the tests==, not when you run them. --- ### THINKING ABOUT TESTS > How do you know that what you’re about to do is a good thing 🤔❓ The answer is that you can’t know that. No one can. ==But thinking about tests can make it more likely.== <br/> **TASK**: Query the database to return a list of people who watch more than 10 videos a week on your “world’s funniest dishwashing videos”site. ```=pyhton def return_avid_viewers do # ... hmmm ... end ``` Start by imagining that you’d finished writing the function and now had to test it. How would you do that? 🧠 **Thinking process** * test data ? * some frameworks can help? or in our case need to query database? * passing the database instance into our function rather than using some global one. ⬇️⬇️⬇️ ```=pyhton def return_avid_users(db) do ``` * how we’d populate that test data? * we look at the database schema for fields (`opened_video` and `completed_video`.) * But we don’t know what the requirement means, and our business contact is out. * pass in the name of the field (which will allow us to test what we have, and potentially change it later): ⬇️⬇️⬇️ ```=pyhton def return_avid_users(db, qualifying_field_name) do ``` <br/> --- ### TESTS DRIVE CODING Thinking about writing a test for our method made us look at it from the outside, as if we were a client of the code, and not its author. > ⭐️ ==A Test Is the First User of Your Code== the biggest benefit: testing is vital feedback that guides your coding. Before you can test something, you have to understand it. (🌚 sounds silly 🌚) * Starting to code without a clear understanding leads to longer and complex code with inadequate boundary conditions and error handling. Testing helps simplify the logic and identify patterns, allowing for better structuring of the function. Think about testing boundary and error conditions beforehand to simplify the code. #### Test-Driven Development(test-driven development or TDD or test-first development.) 1. Decide on a small piece of functionality 2. Write a passing test(確保測試程式可執行) 3. Run all tests, verifying that the new test fails 4. Write minimal code to pass the test 5. Refactor the code and re-run the tests to ensure they pass. 👍🏻 TDD is beneficial for beginners because it guarantees tests for your code, leading to a continuous focus on testing. 👎 However, we’ve also seen people become slaves to TDD. - They spend inordinate amounts of time ensuring that they always have 100% test coverage. - They have lots of redundant tests. - Their designs tend to start at the bottom and work their way up. > ⭐️ ==Don’t forget to stop every now and then and look at the big picture.== It is easy to become seduced by the green "tests passed" message, writing lots of code that doesn’t actually get you closer to a solution. --- ### TDD: YOU NEED TO KNOW WHERE YOU’RE GOING ![](https://i.imgur.com/kDiBGme.png) > “How do you eat an elephant?” “One bite at a time.” When you can’t comprehend the whole problem, take small steps, one test at a time. However, this approach can mislead you, encouraging you to focus on and endlessly polish the easy problems while ignoring the real reason you’re coding. 🥸👎🏻 TDD approach: [In 2006, Ron Jeffries began a series of blog posts about his test-driven approach to coding a Sudoku solver.](https://ronjeffries.com/categories/sudoku/) An alternative approach: [Peter Norvig offers an alternative approach, rather than being driven by tests, he starts with a basic understanding of how these kinds of problems are traditionally solved.](http://norvig.com/sudoku.html) > ⭐️ Tests can definitely help drive development. But, as with every drive, unless you have a destination in mind, you can end up going in circles. --- ### BACK TO CODE Component-based development has long been a lofty goal of software development. #### UNIT TESTING Hardware chip-level testing and software unit testing are similar in that they both test modules in isolation to verify behavior. Thorough testing under controlled conditions helps gain confidence in how a module will perform. #### TESTING AGAINST CONTRACT We want to write test cases that ensure that a given unit honors its contract. - whether the code meets the contract - whether the contract means what we think it means. **What does this mean in practice?** **TASK:** A square root routine. ```=pyhton pre-conditions: argument >= 0; post-conditions: ((result * result) - argument).abs <= epsilon*argument; ``` - Reject negative input. - Accept input of zero (boundary value). - Verify that the calculated square root of values between zero and the maximum expressible argument is within a small fraction of the original value (epsilon). We can write a basic test script to exercise the square root function using its contract and assuming that it does its own pre- and postcondition checking. ``` assertWithinEpsilon(my_sqrt(0), 0) assertWithinEpsilon(my_sqrt(2.0), 1.4142135624) assertWithinEpsilon(my_sqrt(64.0), 8.0) assertWithinEpsilon(my_sqrt(1.0e7), 3162.2776602) assertRaisesException fn => my_sqrt(-4.0) end ``` In the real world, any nontrivial module is likely to be dependent on a number of other modules. **EXMAPLE**: We have a module A that uses a DataFeed and a LinearRegression. 1. DataFeed’s contract, in full 2. LinearRegression’s contract, in full 3. A’s contract, which relies on the other contracts but does not directly expose them. ==subcomponents first -> module itself== This technique is a great way to reduce debugging effort: we can quickly concentrate on the likely source of the problem within module A, and not waste time reexamining its subcomponents. #### AD HOC TESTING(臨時測試) Not to be confused with “odd hack”!Ad-hoc testing is when we run poke at our code manually. At the end of the debugging session, you need to formalize this ad hoc test. #### BUILD A TEST WINDOW Despite the best sets of tests, bugs can still surface in production environments. - Log files - a diagnostic control window. ”hot-key” sequence or magic URL. - a feature switch #### A CULTURE OF TESTING You really only have a few choices: - Test First 👍(This is probably the best choice in most circumstances, as it ensures that testing happens.) - Test During - Test Never ==A culture of testing means all the tests pass all the time.== Ignoring consistently failing tests leads to ignoring all tests, starting a vicious spiral. > ⭐️ Test Your Software, or Your Users Will ## Topic 42 Property-Based Testing If you write the original code and you write the tests, is it possible that an incorrect assumption could be expressed in both ❓ The code passes the tests, because it does what it is supposed to based on your understanding. - Have different people write tests and the code under test. 👎🏻 - Computer does some testing for you 👍 --- ### CONTRACTS(合約), INVARIANTS(不變數), AND PROPERTIES(屬性) > ⭐️ Use Property-Based Tests to Validate Your Assumptions - CONTRACTS (you feed it input, and it will make certain guarantees about the outputs it produces.) - INVARIANTS(state remain true) - PROPERTIES: (CONTRACTS and INVARIANTS) **TASK**: tests for sorted list. - PROPERTIES: 1. the sorted list is the same size as the original. 2. no element in the result can be greater than the one that follows it. ```=pyhton from hypothesis import given import hypothesis.strategies as some @given(some.lists(some.integers())) def test_list_size_is_invariant_across_sorting(a_list): original_length = len(a_list) a_list.sort() assert len(a_list) == original_length @given(some.lists(some.text())) def test_sorted_result_is_ordered(a_list): a_list.sort() for i in range(len(a_list) - 1): assert a_list[i] <= a_list[i + 1] ``` RESULT: ``` $ pytest sort.py ======================= test session starts ======================== ... plugins: hypothesis-4.14.0 sort.py .. [100%] ===================== 2 passed ``` Not much drama there. :::spoiler hypothesis? Hypothesis is an advanced testing library for Python. It lets you write tests which are parametrized by a source of examples, and then generates simple and comprehensible examples that make your tests fail. This lets you find more bugs in your code with less work. ::: --- ### TEST DATA GENERATION 測試資料的生成 We can specify the test data we want to generate. ``` @given(some.lists(some.integers())) ``` ⬇️⬇️⬇️ ``` @given(some.integers(min_value=5, max_value=10).map(lambda x: x * 2)) ``` --- ### FINDING BAD ASSUMPTIONS **TASK**: A simple order processing and stock control system. Warehouse. We can query a warehouse to see if something is in stock, remove things from stock, and get the current stock levels. ```=python class Warehouse: def __init__(self, stock): self.stock = stock <!-- 是否有庫存 --> def in_stock(self, item_name): return (item_name in self.stock) and (self.stock[item_name] > 0) <!-- 從庫存中移除 --> def take_from_stock(self, item_name, quantity): if quantity <= self.stock[item_name]: self.stock[item_name] -= quantity else: raise Exception("Oversold {}".format(item_name)) <!-- 從庫存數量--> def stock_count(self, item_name): return self.stock[item_name] ``` TEST: ✅ PASS ```=pyhton def test_warehouse(): wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0}) assert wh.in_stock("shoes") assert wh.in_stock("hats") assert not wh.in_stock("umbrellas") wh.take_from_stock("shoes", 2) assert wh.in_stock("shoes") wh.take_from_stock("hats", 2) assert not wh.in_stock("hats") ``` TASK: A function that processes a request to order items from the warehouse. ```=pyhton def order(warehouse, item, quantity): if warehouse.in_stock(item): warehouse.take_from_stock(item, quantity) return ( "ok", item, quantity ) else: return ( "not available", item, quantity ) ``` TEST: ✅ PASS ```=pyhton def test_order_in_stock(): wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0}) status, item, quantity = order(wh, "hats", 1) assert status == "ok" assert item == "hats" assert quantity == 1 assert wh.stock_count("hats") == 1 def test_order_not_in_stock(): wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0}) status, item, quantity = order(wh, "umbrellas", 1) assert status == "not available" assert item == "umbrellas" assert quantity == 1 assert wh.stock_count("umbrellas") == 0 def test_order_unknown_item(): wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0}) status, item, quantity = order(wh, "bagel", 1) assert status == "not available" assert item == "bagel" assert quantity == 1 ``` #### everything looks fine. let’s add some property tests. 1. stock = taken items + remaining items; In the following test, we run our test with the item parameter chosen randomly from "hat" or "shoe" and the quantity chosen from 1 to 4: ```=python @given(item = some.sampled_from(["shoes", "hats"]), quantity = some.integers(min_value=1, max_value=4)) def test_stock_level_plus_quantity_equals_original_stock_level(item, quantity): wh = Warehouse({"shoes": 10, "hats": 2, "umbrellas": 0}) initial_stock_level = wh.stock_count(item) (status, item, quantity) = order(wh, item, quantity) if status == "ok": assert wh.stock_count(item) + quantity == initial_stock_level ``` TEST: :::danger 🚫 Fail: we tried to remove three hats from the warehouse, but it only has two in stock. ::: ==Our property testing found a faulty assumption==: our in_stock function only checks that there’s at least one of the given item in stock. Instead we need to make sure we have enough to fill the order👍 : ```=python def in_stock(self, item_name, quantity): » return (item_name in self.stock) and (self.stock[item_name] >= quantity) ``` ```=python def order(warehouse, item, quantity): » if warehouse.in_stock(item, quantity): warehouse.take_from_stock(item, quantity) return ( "ok", item, quantity ) else: return ( "not available", item, quantity ) ``` :::spoiler Detail ```= $ pytest stock.py . . . stock.py:72: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ stock.py:76: in test_stock_level_plus_quantity_equals_original_stock_level (status, item, quantity) = order(wh, item, quantity) stock.py:40: in order warehouse.take_from_stock(item, quantity) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <stock.Warehouse object at 0x10cf97cf8>, item_name = 'hats' quantity = 3 def take_from_stock(self, item_name, quantity): if quantity <= self.stock[item_name]: self.stock[item_name] -= quantity else: > raise Exception("Oversold {}".format(item_name)) E Exception: Oversold hats stock.py:16: Exception ---------------------------- Hypothesis ---------------------------- Falsifying example: test_stock_level_plus_quantity_equals_original_stock_level( item='hats', quantity=3) ``` ::: --- ### PROPERTY-BASED TESTS OFTEN SURPRISE YOU It’s powerful because you set up some rules for generating inputs, set up some assertions for validating output, and then just let it rip. --- ### PROPERTY-BASED TESTS ALSO HELP YOUR DESIGN :::success ⭐️ A unit test is the first client of your API. ⭐️ Property-based tests make you think about your code in terms of invariants and contracts. ::: Try using both. --- ### EXERCISES 1. Look back at the warehouse example. Are there any other properties that you can test? :::spoiler ANSWER One property we can test is that an order succeeds if the warehouse has enough items on hand. We can generate orders for random quantities of items, and verify that an "OK" tuple is returned if the warehouse had stock. ::: 2. Your company ships machinery. Each machine comes in a crate, and each crate is rectangular. The crates vary in size. Your job is to write some code to pack as many crates as possible in a single layer that fits in the delivery truck. The output of your code is a list of all the crates. For each crate, the list gives the location in the truck, along with the width and height. What properties of the output could be tested? :::spoiler ANSWER - Do any two crates overlap? - Does any part of any crate exceed the width or length of the truck? - Is the packing density (area used by crates divided by the area of the truck bed) less than or equal to 1? - If it’s part of the requirement, does the packing density exceed the minimum acceptable density? :::

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully