Skip to content
tpmai edited this page Nov 17, 2021 · 66 revisions

Lab 8

Due Date

Friday November 12 by Midnight.

Overview

This week we are focused on managing project complexity through the use of Automated Testing. Software testing is a huge subject, and should really be its own course (or multiple courses!). Learning to write good test cases, and more, learning to write testable code, takes a lot of practice. It takes experience to understand many of the ways that software can fail, and how to defend against it.

Testing helps a large community of developers (or even a single developer) keep a piece of software evolving in the right direction. Without tests, it's easy to break existing code, introduce unexpected bugs, or ignore important edge cases. At first, testing our software manually seems feasible. However, manual approaches quickly break down as the software grows in size and complexity.

NOTE: I highly recommend watching this week's video lectures before you start this lab.

This lab will help you learn to work with and set up the following:

  • general concepts and terminology of software testing
  • unit testing and integration testing

For this lab you will are asked to work on your SSG project. However, you are free to collaborate and help each other as you do these steps. Make use of the course's community on Slack and GitHub.

Step 1: Choose and Set Up a Testing Framework

Begin by doing some research to choose a testing framework to use in your project. Sometimes the language or libraries you use will dictate an obvious choice. Other times you need to decide among a series of options. For this lab, I would recommend that you choose the most obvious/popular choice for your language/platform. This will help you find examples, documentation, and StackOverflow responses to questions you might also have.

Here are some suggested resources for picking your testing framework:

Once you have made your choice, create a testing branch and set up your framework. Read the documentation for your chosen framework. You might also find it helpful to look at other open source projects that are similar to your project and use this framework.

When you are able to run the test framework in your project, commit your work to the testing branch and proceed to the next step.

Step 2: Write Your First Test

Choose the simplest area of your code that you can to begin testing. I would recommend choosing a basic function or method that you can use to write unit tests. Follow the guidelines for your chosen testing framework to add a new test file for this function and add some test cases.

In your test cases, consider the following:

  • If the function accepts multiple arguments, create separate test cases for the various combinations of arguments. For example, if the function accepts a string and an optional boolean, you should have a test case for a string being passed with the boolean argument, and another for the case that it is omitted.
  • Consider the code paths through your function. Do you have any conditionals (if statements, ternary operators, etc.)? If so, make sure you write test cases that cover all pathways through your code. Typically this means passing different types of data to the function, result in specific choices being made within the function's implementation.
  • Consider the possible "good" values your function might accept. If it takes a number, what does a valid number look like? Write cases for these "good" values. Try not to repeat the same code over and over again (e.g., don't write a test for 7, 8, and 9, since they all do essentially the same thing).
  • Consider the possible "bad" values your function might encounter. If you accept a reference to an object, what happens if you get null instead? If you accept an array, what happens if the array is empty? If you accept a string, what happens if it is empty? What happens if properties are missing on an object? Try to think like a pessimist and consider all the ways the world might conspire to break your function.
  • Consider the proper return type of your function (if any). Given a particular set of input, what do you expect to get back? Write test cases that cover all possible "good" return values.
  • Consider any extreme error cases. For example, does your function throw in certain conditions? Write test cases that cause it to throw, and check that it did. Make sure your function fails in the way you expect.

Even a small function will often have a half-dozen or more test cases that are necessary in order to prove that it works as expected. Try not to go overboard and write too much testing code; but find a balance, such that you know your function works for the cases you've considered.

Run your test runner and make sure that your test is executed. Did any tests fail? Fix the tests or code (or both) so that things work as you expect. Do no tests fail? Try changing your function to confirm that the tests do in fact fail when things break.

After you are satisfied that things work, run your tests one last time to make sure everything passes, then commit your work to the testing branch and proceed to the next step.

Step 2b: (Optional) Test Runner Improvements

Running your tests can become tedious if you have to always run every test. What if you are working on a particular file/function, and want to run a single test over and over again while you fix a bug?

Find a way to run a single test or test file.

Find a way to have your test runner watch for changes and run tests automatically when the test or source code is updated.

Document how to do both of these in your CONTRIBUTING.md file.

Step 3: Test the Core of your SSG

Your SSG is designed to read text files and produce HTML. You should write at least one test that takes a simple piece of text and runs it through your parser, producing HTML output. One way to look at this kind of test is: given input A, calling function f(A) should produce output B. We can write lots of little tests like this, once we get the hang of it, in order to test various edge cases (e.g., text with a title, text with a paragraph, text with no title, ...).

Writing this test might require you to restructure your existing code: your program is currently designed to be run as a CLI tool, manually by a person. However, when we write tests, we usually want to work with classes, objects, or functions automatically in code. If you write your code so that it is broken up into functions/classes that can be included in your tests, it makes it easier to write tests. For example, instead of working directly with the CLI parser, you might use the functions that parse and generate HTML directly in your tests.

A central question that good developers ask of their code is, "How will I test this?" Often the answer requires refactoring in order to break up large pieces of code and expose the bits that we need to access in tests.

Remember: tests should be small. Don't write a test that tests everything. Slowly work toward testing everything by testing all the little things one by one. Eventually you'll have covered everything.

Step 4. Update Developer Docs to Include Testing Details

Update your project's CONTRIBUTING.md file to include information about running your test framework and how you want people to organize and write tests in your project. The easier you make it for developers to include tests, the more likely it is that they will.

Step 5: (Optional) Add Code Coverage Analysis

It's important to make sure that you are including test cases for as many code paths as possible. However, how do you know when you're done? How many test cases are enough? 10? 100? 1000!?

The truth is "it depends." Instead of guessing we need a way to analyze our tests and implementation code, and determine how much coverage we have; that is: how much of our implementation is being executed during our test run, and more importantly, which parts are being missed? If we know the lines that are being missed, writing a test to add coverage is often fairly trivial (e.g., an if-else block that checks for a variable being true or false might only require us to pass false as an argument).

Research a code coverage approach for your chosen testing framework. There isn't one way to do this for all programming languages, so you'll need to find the appropriate tools to use and integrate with your project. You should be able to run a script or command and have a coverage report generated for you. This might be plain text or an HTML file or something else. Typically we do not include this report in git (i.e., add a .gitignore file and include the filename(s) or folder(s) to exclude), since the coverage information is generated.

When you are able to run your coverage command/script and generate a report, determine if there are any obvious test cases you should add for your two test cases above. Did you miss any code paths? Are any lines of code being missed?

Step 6: Merge testing to master

When you're done, squash and merge your testing branch into your main branch. This means doing something like the following:

# switch to the testing branch
$ git checkout testing
# do an interactive rebase on master and squash your commits into a single commit
$ git rebase master -i
# switch over to the master branch 
$ git checkout master
# merge the testing branch into master doing a fast-forward merge (no merge commit)
$ git merge --ff-only testing
# push this new work up to GitHub
$ git push origin master

Once you have confirmed that all of your testing code and tests have been included on your project's master branch on GitHub, you can proceed to the next step.

Step 7: Write a Blog Post

When you have completed all of the required steps above, please write a detailed blog post. In your post, discuss the following:

  • Which testing framework/tools did you choose? Why did you choose them? Provide links to each tool and briefly introduce them.
  • How did you set them up in your project? Be detailed so that other developers could read your blog and get some idea how to do the same.
  • What did you learn while writing your test cases? Did you have any "aha!" moments or get stuck?
  • Did your tests uncover any interesting bugs or edge cases?
  • What did you learn from this process? Had you ever done testing before? Do you think you'll do testing on projects in the future?

Submission

When you have completed all the requirements above, please add your details to the table below.

Name Blog Post (URL) Testing commit
Roman Rezinkin Blog 4b18e4b
Tuan Phong Nguyen https://blog.andrewnt.dev/post/osd-600-lab-8 706edbc1
Minsu Kim https://dev.to/mkim219/unit-test-for-ssg-1370 commit
Ahmad Rokay https://dev.to/ar/testing-lab-8-3nnc 4e9d7040
Gustavo Tavares https://dev.to/gmotgit/osd600-lab08-42cb adcdedea
Chi Kien Nguyen https://dev.to/kiennguyenchi/catch2-testing-framework-2cdg commit
Joshua Li https://dev.to/jli/adding-tests-to-lennah-540m 1772302
Thanh Cong Van https://dev.to/tcvan0707/osd600-lab-8-3l64 66c2731
Anatoliy Serputov https://medium.com/@aserputov/testing-33fb753be08d d96546
Hung Nguyen https://dev.to/nguyenhung15913/testing-for-static-site-generator-2jbo b066241
Kunwarvir Dhillon https://dev.to/dhillonks/unit-testing-with-jest-77m 3d3d110
Andre Willomitzer https://dev.to/andrewillomitzer/jest-execution-testing-automated-setup-502k 7d1f038
Xiongye Jiang https://dev.to/derekjxy/testing-in-javascript-46ao [025d9a8], [Update CONTRIBUTING]
Kevan Yang https://dev.to/pandanoxes/lab-8-add-testing-jest-3def 2ca3d50
Tuan Thanh Tan https://dev.to/tuanthanh2067/adding-tests-for-my-static-site-generator-using-jest-2gpc 8ab2da
Oliver Pham https://dev.to/oliverpham/how-i-set-up-testing-for-my-python-project-5c13 5a5590a
Irene Park https://dev.to/irenejoeunpark/unit-testing-using-junit-5bio cb9a626
Japneet Singh Kalra https://dev.to/japneetsingh035/adding-testing-integration-in-swift-using-xctest-5cb5 6b87fc
Leyang Yu https://dev.to/lyu4321/testing-using-jest-3ok1 b9b9d81
Minh Quan Nguyen https://dev.to/mqnguyen/automated-testing-with-jest-4745 2bf6d27
Gus McCallum Blog Post Commit
Le Minh Pham Blog Post Commit
Francesco Menghi https://dev.to/menghif/code-testing-with-jest-3b4h b5ed6ec
Roxanne Lee https://medium.com/@Roxanne.Lee/code-tests-31e68754ef9a c580bd3
Vivian Vu https://dev.to/vivianvu/osd600-adding-testing-tool-m7e 6313fab
Minh Hang Nguyen https://dev.to/minhhang107/add-testing-to-ssg-1d58 ed65ee9
Amasia Nalbandian blog c9cd868
Andrew Tassone https://dev.to/drew5494/adding-google-test-the-great-site-generator-53l9 4988926
Mizuho Okimoto https://dev.to/okimotomizuho/test-my-static-site-generator-with-jest-4o4f e6519eb
Tue Nguyen https://dev.to/tuenguyen2911_67/testing-my-application-4j6 d713635
Ritik Bheda https://dev.to/ritikbheda/adding-testing-3b09 commit
Trang Nguyen https://tracy016.medium.com/osd600-unit-testing-47877e240154 4b18e4b
Andrei Batomunkuev https://medium.com/@andreibatomunkuev/automated-testing-c2e02d82335c ba6acaf, 6af8131
Tengzhen Zhao https://dev.to/yodacanada/lab8-manage-project-complexity-through-the-use-of-automated-testing-1hka 664a896
Duc Bui Manh https://medium.com/@manhducdkcb/have-fun-writing-tests-in-typescript-788af8452a80 816d9a7
Diana Belokon Blog Commit
Emily Phan Blog Commit
Gerardo Enrique Arriaga Rendon Testing yassgy e9e2465
Suhhee Kim Creating test plan test plan created, contribuion.md file updated
Jun Song Test plan Commit
Alex Romanova Blog PR
Qiwen Yu Blog 8c864d9
Eugene Chung Blog 3217316
Jiyun Jung Blog 85a497b
Luigi Zaccagnini Creating Tests for Octo a7d32dc
James Mai Blog Commit