Repository: hhursev/recipe-scrapers
Pull request #61 (2019-06-09 11:38:31)

Added serving-size
Most of the given pages already have serving-size on the page. It gives a better overview when getting recipes when possible. Some pages, like *All Recipes (BR)*, *The vintage mixer* and *What's Gaby cooking* do not have this possibility, no testcase created but will return 0. 

The `_utils.get_servings` is not clean logic, it tries to see if the given element is for serving (defaults to serving) or items. Might not be the best practice.  

hhursev: Woah man 🍻  This is a really nice job! The code looks spot on.

The only thing that I find strange is the one you've been upfront about - get_servings returning either int or str.

If I understand correct, serving is more like a portion you eat on a single sitting and items is used when we talk about cakes, sandwiches etc.

I believe the best approach would be to rename the method to `yields` and make it return a str. The result may be "4 servings", "5 items", "8 portions" etc. The method defaults should default to empty str.

Let me know if this makes sense and you agree with the logic.

I really appreciate the input. The code is 🥇 👍 
Jwe0619: That's great input, got a bit blind by "serving".
I simplified it, since I want it to be easy to get the serving size when it exists. So yields will return "x serving(s)" if it's of "portions" and "item(s)".

This is not 100% correct though, like jamie olivers test-case it says "serving(s)" when it should be "item(s)", but this is because the page literally says "Serves" for the items. So it is not without an error rate, but it is redundant since some common sense can be used (if it is a muffins recipe and it has 20 servings, the human conclusion should be that we are talking about number of muffins). 

So the debate is rather if it should have "item(s)" or only "serving(s)", but I feel it is nice to have items-category when possible. Let me know what you think!
hhursev: I'd expect this package to parse what is on the site without playing it extra clever. Even for the oliver's site I'd make it dumb and just return "5 serve(s)".

That being said I'd expect the `yields` method to return:

`2 item(s)`
`1 serve(s)`
`5 serving(s)`

And whatever the site is using for that recipe. That way people using the package can determine the categories by `split(' ')[1]` and unite them however they find fit. I can see how different scenarios would require different grouping.

lmk your thoughts on this and feel free to add the new method in the README.md file too! I'd like to merge this  :)
Jwe0619: To be fair, it is that dumb, I matched "dumbness level" with the `total_time`. It takes the text and format it to a text that is easy to split as you said. The reason why I formatted it is because it's arbitrary how the text is represented, almost no page presents it text-wise the same. I just make sure that it is `<#> <helptext>`, and it will be serving(s) in 90% of the cases. The reason why some returns are item(s) as the helptext is mostly an hint that we are probably not talking about servings without any guarantees. If people want to use that or not is outside the scoop of the package.  So yeah, it is dumb alright.

But if you really want it dumb I'll just have it return the integer without any help-string attached.

Patch:
diff --git a/README.md b/README.md
index 213e0f43..d98601b8 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@ then:
 
     scraper.title()
     scraper.total_time()
+    scraper.yields()
     scraper.ingredients()
     scraper.instructions()
     scraper.links()
diff --git a/recipe_scrapers/_abstract.py b/recipe_scrapers/_abstract.py
index 7e3056b4..9974cf68 100644
--- a/recipe_scrapers/_abstract.py
+++ b/recipe_scrapers/_abstract.py
@@ -24,6 +24,7 @@ class AbstractScraper():
             decorated_methods = [
                 'title',
                 'total_time',
+                'yields',
                 'instructions',
                 'ingredients',
                 'links'
@@ -32,6 +33,8 @@ class AbstractScraper():
                 to_return = ''
             if name == 'total_time':
                 to_return = 0
+            if name == 'yields':
+                to_return = ''
             if name == 'ingredients':
                 to_return = []
             if name == 'links':
@@ -73,6 +76,10 @@ class AbstractScraper():
         """ total time it takes to preparate the recipe in minutes """
         raise NotImplementedError("This should be implemented.")
 
+    def yields(self):
+        """ The number of servings or items in the recipe """
+        raise NotImplementedError("This should be implemented.")
+
     def ingredients(self):
         raise NotImplementedError("This should be implemented.")
 
diff --git a/recipe_scrapers/_utils.py b/recipe_scrapers/_utils.py
index ba965988..03aa7d83 100644
--- a/recipe_scrapers/_utils.py
+++ b/recipe_scrapers/_utils.py
@@ -7,6 +7,18 @@ TIME_REGEX = re.compile(
     r'(\D*(?P<hours>\d+)\s*(hours|hrs|hr|h|Hours|H))?(\D*(?P<minutes>\d+)\s*(minutes|mins|min|m|Minutes|M))?'
 )
 
+SERV_REGEX_NUMBER = re.compile(
+    r'(\D*(?P<items>\d+)?\D*)'
+)
+
+SERV_REGEX_ITEMS = re.compile(
+    r'\bsandwiches\b |\btacquitos\b | \bmakes\b', flags=re.I | re.X
+)
+
+SERV_REGEX_TO = re.compile(
+    r'\d+(\s+to\s+|-)\d+', flags=re.I | re.X
+)
+
 
 def get_minutes(element):
     try:
@@ -23,6 +35,38 @@ def get_minutes(element):
         return 0
 
 
+def get_yields(element):
+    """
+    Will return a string of servings or items, if the receipt is for number of items and not servings
+    the method will return the string "x item(s)" where x is the quantity.
+    :param element: Should be BeautifulSoup.TAG, in some cases not feasible and will then be text.
+    :return: The number of servings or items.
+    """
+    try:
+
+        if isinstance(element, str):
+            tstring = element
+        else:
+            tstring = element.get_text()
+
+        if SERV_REGEX_TO.search(tstring):
+            tstring = tstring.split(SERV_REGEX_TO.split(tstring)[1])[1]
+
+        matched = SERV_REGEX_NUMBER.search(tstring).groupdict().get('items') or 0
+        servings = "{} serving(s)".format(matched)
+
+        if SERV_REGEX_ITEMS.search(tstring) is not None:
+            # This assumes if object(s), like sandwiches, it is 1 person.
+            # Issue: "Makes one 9-inch pie, (realsimple-testcase, gives "9 items")
+            servings = "{} item(s)".format(matched)
+
+        return servings
+
+    except AttributeError as e:  # if dom_element not found or no matched
+        print("get_serving_numbers error {}".format(e))
+        return ''
+
+
 def normalize_string(string):
     return re.sub(
         r'\s+', ' ',
diff --git a/recipe_scrapers/allrecipes.py b/recipe_scrapers/allrecipes.py
index 36d620f2..605d97b5 100644
--- a/recipe_scrapers/allrecipes.py
+++ b/recipe_scrapers/allrecipes.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class AllRecipes(AbstractScraper):
@@ -17,6 +17,12 @@ class AllRecipes(AbstractScraper):
             {'class': 'ready-in-time'})
         )
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'meta',
+            {'id': 'metaRecipeServings', 'itemprop': 'recipeYield'}).get("content")
+                          )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/allrecipesbr.py b/recipe_scrapers/allrecipesbr.py
index 7b412fe5..9af87032 100644
--- a/recipe_scrapers/allrecipesbr.py
+++ b/recipe_scrapers/allrecipesbr.py
@@ -30,6 +30,9 @@ class AllRecipesBr(AbstractScraper):
             for ingredient in ingredients
         ]
 
+    def yields(self):
+        return 0
+
     def instructions(self):
         instructions = self.soup.find(
             'ol', {'itemprop': 'recipeInstructions'}
diff --git a/recipe_scrapers/bbcfood.py b/recipe_scrapers/bbcfood.py
index d1c8dcd4..327be7d1 100644
--- a/recipe_scrapers/bbcfood.py
+++ b/recipe_scrapers/bbcfood.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class BBCFood(AbstractScraper):
@@ -24,6 +24,12 @@ class BBCFood(AbstractScraper):
             )
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'p',
+            {'class': 'recipe-metadata__serving'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/bbcgoodfood.py b/recipe_scrapers/bbcgoodfood.py
index dec1e9c4..a4e56b4a 100644
--- a/recipe_scrapers/bbcgoodfood.py
+++ b/recipe_scrapers/bbcgoodfood.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class BBCGoodFood(AbstractScraper):
@@ -24,6 +24,12 @@ class BBCGoodFood(AbstractScraper):
             ).find('span'))
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+                'span',
+                {'class': 'recipe-details__text', 'itemprop': 'recipeYield'}
+            ))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/bonappetit.py b/recipe_scrapers/bonappetit.py
index 060e053e..e89777e7 100644
--- a/recipe_scrapers/bonappetit.py
+++ b/recipe_scrapers/bonappetit.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import normalize_string
+from ._utils import normalize_string, get_yields
 
 
 class BonAppetit(AbstractScraper):
@@ -17,6 +17,14 @@ class BonAppetit(AbstractScraper):
     def total_time(self):
         return 0
 
+    def yields(self):
+        return get_yields(
+            self.soup.find(
+                'span',
+                {'class': "recipe__header__servings recipe__header__servings--basically"}
+            ).find('span')
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/closetcooking.py b/recipe_scrapers/closetcooking.py
index acb0637f..0a34e194 100644
--- a/recipe_scrapers/closetcooking.py
+++ b/recipe_scrapers/closetcooking.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class ClosetCooking(AbstractScraper):
@@ -17,6 +17,10 @@ class ClosetCooking(AbstractScraper):
     def total_time(self):
         return get_minutes(self.soup.find(itemprop='totalTime').parent)
 
+
+    def yields(self):
+        return get_yields(self.soup.find(itemprop='recipeYield').parent)
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/cookstr.py b/recipe_scrapers/cookstr.py
index 05e6c5fb..8c103667 100644
--- a/recipe_scrapers/cookstr.py
+++ b/recipe_scrapers/cookstr.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class Cookstr(AbstractScraper):
@@ -26,6 +26,18 @@ class Cookstr(AbstractScraper):
                 total_time += get_minutes(time.parent.parent)
         return total_time
 
+    def yields(self):
+        sections = self.soup.findAll(
+            'span',
+            {'class': 'attrLabel'}
+        )
+        total_serves = 0
+        for section in sections:
+            serves = section.find(text='Serves')
+            if serves:
+                total_serves += get_yields(serves.parent.parent)
+        return total_serves
+
     def ingredients(self):
         ingredients = self.soup.find(
             'div',
diff --git a/recipe_scrapers/epicurious.py b/recipe_scrapers/epicurious.py
index 439e0381..5ef70abd 100644
--- a/recipe_scrapers/epicurious.py
+++ b/recipe_scrapers/epicurious.py
@@ -2,7 +2,7 @@ import re
 
 
 from ._abstract import AbstractScraper
-from ._utils import normalize_string
+from ._utils import normalize_string, get_yields
 
 
 class Epicurious(AbstractScraper):
@@ -21,6 +21,12 @@ class Epicurious(AbstractScraper):
     def total_time(self):
         return 0
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'dd',
+            {'itemprop': 'recipeYield'}
+        ))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/finedininglovers.py b/recipe_scrapers/finedininglovers.py
index ac83e59d..f5233a79 100644
--- a/recipe_scrapers/finedininglovers.py
+++ b/recipe_scrapers/finedininglovers.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class FineDiningLovers(AbstractScraper):
@@ -20,6 +20,13 @@ class FineDiningLovers(AbstractScraper):
             {'itemprop': 'prepTime'})
         )
 
+
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span',
+            {'itemprop': 'recipeYield'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/foodnetwork.py b/recipe_scrapers/foodnetwork.py
index def755a9..95103192 100644
--- a/recipe_scrapers/foodnetwork.py
+++ b/recipe_scrapers/foodnetwork.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class FoodNetwork(AbstractScraper):
@@ -17,6 +17,13 @@ class FoodNetwork(AbstractScraper):
             {'class': 'm-RecipeInfo__a-Description--Total'})
         )
 
+    def yields(self):
+        return get_yields(self.soup.find(
+                'ul',
+                {'class': 'o-RecipeInfo__m-Yield'}
+            ).find('span', {'class': 'o-RecipeInfo__a-Description'})
+                          )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'p',
diff --git a/recipe_scrapers/foodrepublic.py b/recipe_scrapers/foodrepublic.py
index 3aff0379..a87f537e 100644
--- a/recipe_scrapers/foodrepublic.py
+++ b/recipe_scrapers/foodrepublic.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class FoodRepublic(AbstractScraper):
@@ -24,6 +24,12 @@ class FoodRepublic(AbstractScraper):
             )
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span',
+            {'itemprop': 'recipeYield'}
+        ))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/geniuskitchen.py b/recipe_scrapers/geniuskitchen.py
index 524f1715..e414df72 100644
--- a/recipe_scrapers/geniuskitchen.py
+++ b/recipe_scrapers/geniuskitchen.py
@@ -1,5 +1,6 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
+
 
 class GeniusKitchen(AbstractScraper):
 
@@ -16,6 +17,13 @@ class GeniusKitchen(AbstractScraper):
             {'class': 'time'})
         )
                 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'td',
+            {'class': 'servings'}
+        ).find('span', {'class': 'count'})
+                          )
+
 
     def ingredients(self):
         ingredients = []
diff --git a/recipe_scrapers/giallozafferano.py b/recipe_scrapers/giallozafferano.py
index 490d3ab3..5cf5cfd4 100644
--- a/recipe_scrapers/giallozafferano.py
+++ b/recipe_scrapers/giallozafferano.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 import json
 
 
@@ -25,6 +25,12 @@ class GialloZafferano(AbstractScraper):
             )
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+                'li',
+                {'class': 'yield'})
+            )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'dd',
diff --git a/recipe_scrapers/hellofresh.py b/recipe_scrapers/hellofresh.py
index eb064328..5c8d5fea 100644
--- a/recipe_scrapers/hellofresh.py
+++ b/recipe_scrapers/hellofresh.py
@@ -20,6 +20,9 @@ class HelloFresh(AbstractScraper):
             {'data-translation-id': "recipe-detail.preparation-time"}
         ).parent.parent)
 
+    def yields(self):
+        return 0
+
     def ingredients(self):
         ingredients_container = self.soup.find(
             'div',
diff --git a/recipe_scrapers/hundredandonecookbooks.py b/recipe_scrapers/hundredandonecookbooks.py
index 958d5ec8..e7b13d1b 100644
--- a/recipe_scrapers/hundredandonecookbooks.py
+++ b/recipe_scrapers/hundredandonecookbooks.py
@@ -17,6 +17,9 @@ class HundredAndOneCookbooks(AbstractScraper):
             {'class': 'preptime'})
         )
 
+    def yields(self):
+        return 0
+
     def ingredients(self):
         ingredients = self.soup.find(
             'div',
diff --git a/recipe_scrapers/inspiralized.py b/recipe_scrapers/inspiralized.py
index 87244fc9..bc956058 100644
--- a/recipe_scrapers/inspiralized.py
+++ b/recipe_scrapers/inspiralized.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class Inspiralized(AbstractScraper):
@@ -17,6 +17,12 @@ class Inspiralized(AbstractScraper):
             {'itemprop': 'totalTime'})
         )
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span',
+            {'itemprop': 'recipeYield'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/jamieoliver.py b/recipe_scrapers/jamieoliver.py
index 9dd5b4a1..4196aa50 100644
--- a/recipe_scrapers/jamieoliver.py
+++ b/recipe_scrapers/jamieoliver.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class JamieOliver(AbstractScraper):
@@ -17,6 +17,12 @@ class JamieOliver(AbstractScraper):
             {'class': 'time'})
         )
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'div',
+            {'class': 'recipe-detail serves'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'ul',
diff --git a/recipe_scrapers/mybakingaddiction.py b/recipe_scrapers/mybakingaddiction.py
index edf23cb0..be79a8bd 100644
--- a/recipe_scrapers/mybakingaddiction.py
+++ b/recipe_scrapers/mybakingaddiction.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class MyBakingAddiction(AbstractScraper):
@@ -24,6 +24,12 @@ class MyBakingAddiction(AbstractScraper):
             ).parent)
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+                'span',
+                {'itemprop': 'recipeYield'}
+            ))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/nihhealthyeating.py b/recipe_scrapers/nihhealthyeating.py
index 9c281573..ec9116ee 100644
--- a/recipe_scrapers/nihhealthyeating.py
+++ b/recipe_scrapers/nihhealthyeating.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import normalize_string, get_minutes
+from ._utils import normalize_string, get_minutes, get_yields
 
 
 class NIHHealthyEating(AbstractScraper):
@@ -22,6 +22,22 @@ class NIHHealthyEating(AbstractScraper):
             for td in time_table.find_all('td')
         ])
 
+    def yields(self):
+        time_table = self.soup.find(
+            'table',
+            {'class': 'recipe_time_table'}
+        )
+
+        i = 0
+        for t in time_table.findAll('th'):
+            if "Yields" in t:
+                break
+            i += 1
+
+        if i >= len(time_table.findAll('td')):
+            return 0
+        return get_yields(time_table.find_all('td')[i])
+
     def ingredients(self):
         ingredients = self.soup.find(
             'div',
diff --git a/recipe_scrapers/paninihappy.py b/recipe_scrapers/paninihappy.py
index c3f35885..217ce2d3 100644
--- a/recipe_scrapers/paninihappy.py
+++ b/recipe_scrapers/paninihappy.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class PaniniHappy(AbstractScraper):
@@ -20,6 +20,12 @@ class PaniniHappy(AbstractScraper):
             {'class': 'duration'})
         )
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span',
+            {'class': 'yield'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/realsimple.py b/recipe_scrapers/realsimple.py
index d9ddd76f..ae71c229 100644
--- a/recipe_scrapers/realsimple.py
+++ b/recipe_scrapers/realsimple.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class RealSimple(AbstractScraper):
@@ -17,6 +17,13 @@ class RealSimple(AbstractScraper):
             {'class': 'recipe-meta-item'}
         )[1])
 
+    def yields(self):
+        return get_yields(self.soup.findAll(
+            'div',
+            {'class': 'recipe-meta-item'}
+        )[2].find('div', {'class': 'recipe-meta-item-body'}).get_text()
+                          )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'div',
diff --git a/recipe_scrapers/simplyrecipes.py b/recipe_scrapers/simplyrecipes.py
index 9214d7c4..4670e978 100644
--- a/recipe_scrapers/simplyrecipes.py
+++ b/recipe_scrapers/simplyrecipes.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class SimplyRecipes(AbstractScraper):
@@ -24,6 +24,12 @@ class SimplyRecipes(AbstractScraper):
             )
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+                'span',
+                {'class': 'yield', 'itemprop': 'recipeYield'})
+            )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'div',
diff --git a/recipe_scrapers/steamykitchen.py b/recipe_scrapers/steamykitchen.py
index 03754f18..8775dee2 100644
--- a/recipe_scrapers/steamykitchen.py
+++ b/recipe_scrapers/steamykitchen.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class SteamyKitchen(AbstractScraper):
@@ -20,6 +20,9 @@ class SteamyKitchen(AbstractScraper):
             get_minutes(self.soup.find(itemprop='cookTime').parent)
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find('span', itemprop='recipeYield'))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'span',
diff --git a/recipe_scrapers/tastesoflizzyt.py b/recipe_scrapers/tastesoflizzyt.py
index 78d1af4a..0a496a15 100644
--- a/recipe_scrapers/tastesoflizzyt.py
+++ b/recipe_scrapers/tastesoflizzyt.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class TastesOfLizzyT(AbstractScraper):
@@ -22,6 +22,14 @@ class TastesOfLizzyT(AbstractScraper):
             )
         )
 
+    def yields(self):
+        return get_yields(
+            self.soup.find(
+                'span',
+                {'class': 'wprm-recipe-servings'}
+            )
+        )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'ul',
diff --git a/recipe_scrapers/tastykitchen.py b/recipe_scrapers/tastykitchen.py
index 19582227..34ce1079 100644
--- a/recipe_scrapers/tastykitchen.py
+++ b/recipe_scrapers/tastykitchen.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class TastyKitchen(AbstractScraper):
@@ -27,6 +27,12 @@ class TastyKitchen(AbstractScraper):
             )
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span',
+            {'itemprop': 'yield'})
+        )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'ul',
diff --git a/recipe_scrapers/thehappyfoodie.py b/recipe_scrapers/thehappyfoodie.py
index 2322bd44..d6a4a021 100644
--- a/recipe_scrapers/thehappyfoodie.py
+++ b/recipe_scrapers/thehappyfoodie.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import normalize_string, get_minutes
+from ._utils import normalize_string, get_minutes, get_yields
 
 
 class TheHappyFoodie(AbstractScraper):
@@ -18,6 +18,12 @@ class TheHappyFoodie(AbstractScraper):
             get_minutes(self.soup.find('div', {'class': 'recipe__data__cook-time'}))
         ])
 
+    def yields(self):
+        #<div class="recipe__data__field recipe__data__yield">
+        return get_yields(self.soup.find(
+            'div', {'class': 'recipe__data__yield'}).get_text()
+                          )
+
     def ingredients(self):
         ingredients = self.soup.find(
             'table', {'class': 'recipe__ingredients-table'}
diff --git a/recipe_scrapers/thepioneerwoman.py b/recipe_scrapers/thepioneerwoman.py
index 9352933d..0b8ca014 100644
--- a/recipe_scrapers/thepioneerwoman.py
+++ b/recipe_scrapers/thepioneerwoman.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class ThePioneerWoman(AbstractScraper):
@@ -23,6 +23,11 @@ class ThePioneerWoman(AbstractScraper):
             ).findAll('dd')
         ])
 
+    def yields(self):
+        return get_yields(self.soup.find(
+            'span', {'itemprop': 'recipeYield'}
+        ))
+
     def ingredients(self):
         ingredients = self.soup.find(
             'ul',
diff --git a/recipe_scrapers/thevintagemixer.py b/recipe_scrapers/thevintagemixer.py
index 2b182b79..77736aa1 100644
--- a/recipe_scrapers/thevintagemixer.py
+++ b/recipe_scrapers/thevintagemixer.py
@@ -19,6 +19,9 @@ class TheVintageMixer(AbstractScraper):
             'meta', {'itemprop': 'totalTime'}).parent
         )
 
+    def yields(self):
+        return 0  # Servings do not exist in this site.
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li', {'itemprop': "recipeIngredient"}
diff --git a/recipe_scrapers/twopeasandtheirpod.py b/recipe_scrapers/twopeasandtheirpod.py
index cd19a5b2..26390359 100644
--- a/recipe_scrapers/twopeasandtheirpod.py
+++ b/recipe_scrapers/twopeasandtheirpod.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class TwoPeasAndTheirPod(AbstractScraper):
@@ -20,6 +20,13 @@ class TwoPeasAndTheirPod(AbstractScraper):
             {'itemprop': 'totalTime'}
         ).parent)
 
+    def yields(self):
+        #<span itemprop="recipeYield">
+        return get_yields(self.soup.find(
+            'span',
+            {'itemprop': 'recipeYield'}
+        ))
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/whatsgabycooking.py b/recipe_scrapers/whatsgabycooking.py
index f45003fa..cb7994a7 100644
--- a/recipe_scrapers/whatsgabycooking.py
+++ b/recipe_scrapers/whatsgabycooking.py
@@ -20,6 +20,9 @@ class WhatsGabyCooking(AbstractScraper):
             {'class': 'ready-in-time'})
         )
 
+    def yields(self):
+        return 0
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',
diff --git a/recipe_scrapers/yummly.py b/recipe_scrapers/yummly.py
index c51c755a..a880fe20 100644
--- a/recipe_scrapers/yummly.py
+++ b/recipe_scrapers/yummly.py
@@ -1,5 +1,5 @@
 from ._abstract import AbstractScraper
-from ._utils import get_minutes, normalize_string
+from ._utils import get_minutes, normalize_string, get_yields
 
 
 class Yummly(AbstractScraper):
@@ -16,6 +16,11 @@ class Yummly(AbstractScraper):
             self.soup.find('div', {'class': 'recipe-summary-item unit'})
         )
 
+    def yields(self):
+        return get_yields(
+            self.soup.find('div', {'class': 'servings'}).find('input').get('value')
+        )
+
     def ingredients(self):
         ingredients = self.soup.findAll(
             'li',


Test patch:
diff --git a/recipe_scrapers/tests/test_allrecipes.py b/recipe_scrapers/tests/test_allrecipes.py
index 04ed0a40..cbf8a827 100644
--- a/recipe_scrapers/tests/test_allrecipes.py
+++ b/recipe_scrapers/tests/test_allrecipes.py
@@ -8,9 +8,7 @@ class TestAllRecipesScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'allrecipes.testhtml'
         )) as file_opened:
@@ -34,6 +32,9 @@ class TestAllRecipesScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual("8 serving(s)", self.harvester_class.yields())
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_bbcfood.py b/recipe_scrapers/tests/test_bbcfood.py
index fb3e9501..b1553bc2 100644
--- a/recipe_scrapers/tests/test_bbcfood.py
+++ b/recipe_scrapers/tests/test_bbcfood.py
@@ -8,9 +8,7 @@ class TestBBCFoodScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'bbc_food.testhtml'
         )) as file_opened:
@@ -40,6 +38,12 @@ class TestBBCFoodScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "1 item(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_bbcgoodfood.py b/recipe_scrapers/tests/test_bbcgoodfood.py
index c99a3eae..584b5570 100644
--- a/recipe_scrapers/tests/test_bbcgoodfood.py
+++ b/recipe_scrapers/tests/test_bbcgoodfood.py
@@ -8,9 +8,7 @@ class TestBBCGoodFoodScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'bbc_good_food.testhtml'
         )) as file_opened:
@@ -34,6 +32,11 @@ class TestBBCGoodFoodScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "12 item(s)",
+            self.harvester_class.yields()
+        )
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_bonappetit.py b/recipe_scrapers/tests/test_bonappetit.py
index bc748b09..1b6c9c07 100644
--- a/recipe_scrapers/tests/test_bonappetit.py
+++ b/recipe_scrapers/tests/test_bonappetit.py
@@ -8,9 +8,7 @@ class TestBonAppetitScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'bonappetit.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestBonAppetitScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_closetcooking.py b/recipe_scrapers/tests/test_closetcooking.py
index d21916e8..951be880 100644
--- a/recipe_scrapers/tests/test_closetcooking.py
+++ b/recipe_scrapers/tests/test_closetcooking.py
@@ -8,9 +8,7 @@ class TestClosetCooking(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'closetcooking.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestClosetCooking(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "5 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_cookstr.py b/recipe_scrapers/tests/test_cookstr.py
index a2e4781e..50181eb0 100644
--- a/recipe_scrapers/tests/test_cookstr.py
+++ b/recipe_scrapers/tests/test_cookstr.py
@@ -8,9 +8,7 @@ class TestCookstrScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'cookstr.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestCookstrScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_total_time(self):
+        self.assertEqual(
+            0,
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_epicurious.py b/recipe_scrapers/tests/test_epicurious.py
index 40bfbb86..314b98bb 100644
--- a/recipe_scrapers/tests/test_epicurious.py
+++ b/recipe_scrapers/tests/test_epicurious.py
@@ -8,9 +8,7 @@ class TestEpicurious(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'epicurious.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestEpicurious(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            0,
+            self.harvester_class.total_time()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_finedininglovers.py b/recipe_scrapers/tests/test_finedininglovers.py
index db222e3b..9f719cf4 100644
--- a/recipe_scrapers/tests/test_finedininglovers.py
+++ b/recipe_scrapers/tests/test_finedininglovers.py
@@ -8,9 +8,7 @@ class TestFineDiningLoversScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'finedininglovers.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestFineDiningLoversScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_foodnetwork.py b/recipe_scrapers/tests/test_foodnetwork.py
index eae1d657..4c692425 100644
--- a/recipe_scrapers/tests/test_foodnetwork.py
+++ b/recipe_scrapers/tests/test_foodnetwork.py
@@ -8,9 +8,7 @@ class TestFoodNetworkScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'foodnetwork.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestFoodNetworkScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_foodrepublic.py b/recipe_scrapers/tests/test_foodrepublic.py
index c2ce31bf..0abc09cc 100644
--- a/recipe_scrapers/tests/test_foodrepublic.py
+++ b/recipe_scrapers/tests/test_foodrepublic.py
@@ -8,9 +8,7 @@ class TestFoodRepublicScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'foodrepublic.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestFoodRepublicScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_geniuskitchen.py b/recipe_scrapers/tests/test_geniuskitchen.py
index 1a47ba80..cd141d8e 100644
--- a/recipe_scrapers/tests/test_geniuskitchen.py
+++ b/recipe_scrapers/tests/test_geniuskitchen.py
@@ -8,9 +8,7 @@ class TestAllRecipesScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'geniuskitchen.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestAllRecipesScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "6 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_giallozafferano.py b/recipe_scrapers/tests/test_giallozafferano.py
index 4c19c2df..d492d2f0 100644
--- a/recipe_scrapers/tests/test_giallozafferano.py
+++ b/recipe_scrapers/tests/test_giallozafferano.py
@@ -8,9 +8,7 @@ class TestGialloZafferanoScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'giallozafferano.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestGialloZafferanoScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "10 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_hellofresh.py b/recipe_scrapers/tests/test_hellofresh.py
index 1180c81f..1abeb6f2 100644
--- a/recipe_scrapers/tests/test_hellofresh.py
+++ b/recipe_scrapers/tests/test_hellofresh.py
@@ -8,9 +8,7 @@ class TestHelloFreshScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'hellofresh.testhtml'
         )) as file_opened:
@@ -40,6 +38,12 @@ class TestHelloFreshScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            0,
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertEqual(
             [
diff --git a/recipe_scrapers/tests/test_hundredandonecookbooks.py b/recipe_scrapers/tests/test_hundredandonecookbooks.py
index 424bf69b..cdacc744 100644
--- a/recipe_scrapers/tests/test_hundredandonecookbooks.py
+++ b/recipe_scrapers/tests/test_hundredandonecookbooks.py
@@ -8,9 +8,7 @@ class TestHundredAndOneCookbooksScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             '101cookbooks.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestHundredAndOneCookbooksScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            0,
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_inspiralized.py b/recipe_scrapers/tests/test_inspiralized.py
index 358ece95..525a7cbb 100644
--- a/recipe_scrapers/tests/test_inspiralized.py
+++ b/recipe_scrapers/tests/test_inspiralized.py
@@ -8,9 +8,7 @@ class TestInspiralizedScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'inspiralized.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestInspiralizedScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "2 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_jamieoliver.py b/recipe_scrapers/tests/test_jamieoliver.py
index a2860b7c..164aaa1f 100644
--- a/recipe_scrapers/tests/test_jamieoliver.py
+++ b/recipe_scrapers/tests/test_jamieoliver.py
@@ -8,9 +8,7 @@ class TestJamieOliverScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'jamieoliver.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestJamieOliverScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "20 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_mybakingaddiction.py b/recipe_scrapers/tests/test_mybakingaddiction.py
index 57a2f7a5..239f90e0 100644
--- a/recipe_scrapers/tests/test_mybakingaddiction.py
+++ b/recipe_scrapers/tests/test_mybakingaddiction.py
@@ -9,9 +9,7 @@ class TestMyBakingAddictionScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'mybakingaddiction.testhtml'
         )) as file_opened:
@@ -35,6 +33,12 @@ class TestMyBakingAddictionScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "10 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_nihhealthyeating.py b/recipe_scrapers/tests/test_nihhealthyeating.py
index 5ba0a2ed..97f5a457 100644
--- a/recipe_scrapers/tests/test_nihhealthyeating.py
+++ b/recipe_scrapers/tests/test_nihhealthyeating.py
@@ -11,9 +11,7 @@ class TestNIHHealthyEatingRecipesScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'nihhealthyeating.testhtml'
         )) as file_opened:
@@ -37,6 +35,12 @@ class TestNIHHealthyEatingRecipesScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "8 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_notimplemented.py b/recipe_scrapers/tests/test_notimplemented.py
index 8c8ece59..fdb5bb5e 100644
--- a/recipe_scrapers/tests/test_notimplemented.py
+++ b/recipe_scrapers/tests/test_notimplemented.py
@@ -12,9 +12,7 @@ class TestNotImplemented(unittest.TestCase):
             pass
 
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'paninihappy.testhtml'
         )) as file_opened:
diff --git a/recipe_scrapers/tests/test_paninihappy.py b/recipe_scrapers/tests/test_paninihappy.py
index 1b6afe39..5e3d494a 100644
--- a/recipe_scrapers/tests/test_paninihappy.py
+++ b/recipe_scrapers/tests/test_paninihappy.py
@@ -8,9 +8,7 @@ class TestPaniniHappyScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'paninihappy.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestPaniniHappyScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 item(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_realsimple.py b/recipe_scrapers/tests/test_realsimple.py
index 4876b961..18657d2e 100644
--- a/recipe_scrapers/tests/test_realsimple.py
+++ b/recipe_scrapers/tests/test_realsimple.py
@@ -8,9 +8,7 @@ class TestRealSimpleScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'realsimple.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestRealSimpleScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "9 item(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_simplyrecipes.py b/recipe_scrapers/tests/test_simplyrecipes.py
index 6a591134..2d21da53 100644
--- a/recipe_scrapers/tests/test_simplyrecipes.py
+++ b/recipe_scrapers/tests/test_simplyrecipes.py
@@ -8,9 +8,7 @@ class TestSimplyRecipesScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'simpyrecipes.testhtml'
         )) as file_opened:
@@ -34,6 +32,13 @@ class TestSimplyRecipesScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        # 6 to 8 servings (makes about 3 quarts), debatable it should be 8 servings.
+        self.assertEqual(
+            "8 item(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_steamykitchen.py b/recipe_scrapers/tests/test_steamykitchen.py
index b05f86ea..51b131fc 100644
--- a/recipe_scrapers/tests/test_steamykitchen.py
+++ b/recipe_scrapers/tests/test_steamykitchen.py
@@ -8,9 +8,7 @@ class TestSteamyKitchenScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'steamykitchen.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestSteamyKitchenScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "10 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertListEqual(
             [
diff --git a/recipe_scrapers/tests/test_tastesoflizzyt.py b/recipe_scrapers/tests/test_tastesoflizzyt.py
index d1e27425..dc5b8ed5 100644
--- a/recipe_scrapers/tests/test_tastesoflizzyt.py
+++ b/recipe_scrapers/tests/test_tastesoflizzyt.py
@@ -8,9 +8,7 @@ class TestTastesOfLizzyTScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'tastesoflizzyt.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestTastesOfLizzyTScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "60 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_tastykitchen.py b/recipe_scrapers/tests/test_tastykitchen.py
index ede27fff..361b235b 100644
--- a/recipe_scrapers/tests/test_tastykitchen.py
+++ b/recipe_scrapers/tests/test_tastykitchen.py
@@ -8,9 +8,7 @@ class TestTastyKitchenScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'tasty_kitchen.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestTastyKitchenScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "12 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_thehappyfoodie.py b/recipe_scrapers/tests/test_thehappyfoodie.py
index 51ec0ec4..7c906ac1 100644
--- a/recipe_scrapers/tests/test_thehappyfoodie.py
+++ b/recipe_scrapers/tests/test_thehappyfoodie.py
@@ -7,11 +7,8 @@ from recipe_scrapers.thehappyfoodie import TheHappyFoodie
 class TestTheHappyFoodie(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
-        with open(
-            os.path.join(
-                os.getcwd(),
-                'recipe_scrapers',
-                'tests',
+        with open(os.path.join(
+                os.path.dirname(os.path.realpath(__file__)),
                 'test_data',
                 'thehappyfoodie.testhtml',
             )
@@ -30,6 +27,9 @@ class TestTheHappyFoodie(unittest.TestCase):
     def test_total_time(self):
         self.assertEqual(90, self.harvester_class.total_time())
 
+    def test_yields(self):
+        self.assertEqual("6 serving(s)", self.harvester_class.yields())
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_thepioneerwoman.py b/recipe_scrapers/tests/test_thepioneerwoman.py
index 3b87fde7..28e7350c 100644
--- a/recipe_scrapers/tests/test_thepioneerwoman.py
+++ b/recipe_scrapers/tests/test_thepioneerwoman.py
@@ -8,9 +8,7 @@ class TestThePioneerWomanScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'thepioneerwoman.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestThePioneerWomanScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_thevintagemixer.py b/recipe_scrapers/tests/test_thevintagemixer.py
index a3a726d4..f852a4ec 100644
--- a/recipe_scrapers/tests/test_thevintagemixer.py
+++ b/recipe_scrapers/tests/test_thevintagemixer.py
@@ -8,9 +8,7 @@ class TestTheVintageMixerScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'thevintagemixer.testhtml'
         )) as file_opened:
diff --git a/recipe_scrapers/tests/test_twopeasandtheirpod.py b/recipe_scrapers/tests/test_twopeasandtheirpod.py
index 93ae00b0..87435439 100644
--- a/recipe_scrapers/tests/test_twopeasandtheirpod.py
+++ b/recipe_scrapers/tests/test_twopeasandtheirpod.py
@@ -8,9 +8,7 @@ class TestTwoPeasAndTheirPodScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'twopeasandtheirpod.testhtml'
         )) as file_opened:
@@ -35,6 +33,12 @@ class TestTwoPeasAndTheirPodScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "20 item(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertCountEqual(
             [
diff --git a/recipe_scrapers/tests/test_utils_exceptions.py b/recipe_scrapers/tests/test_utils_exceptions.py
index 4cc78302..9e016df9 100644
--- a/recipe_scrapers/tests/test_utils_exceptions.py
+++ b/recipe_scrapers/tests/test_utils_exceptions.py
@@ -15,6 +15,9 @@ class TestingExceptionsHandling(AbstractScraper):
     def total_time(self):
         raise Exception
 
+    def yields(self):
+        raise Exception
+
     def ingredients(self):
         raise Exception
 
@@ -26,9 +29,7 @@ class TestExceptionsHandlingScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'allrecipes.testhtml'
         )) as file_opened:
@@ -45,6 +46,9 @@ class TestExceptionsHandlingScraper(unittest.TestCase):
     def test_total_time(self):
         return self.assertEqual(0, self.harvester_class.total_time())
 
+    def test_yields(self):
+        return self.assertEqual("", self.harvester_class.yields())
+
     def test_ingredients(self):
         return self.assertCountEqual([], self.harvester_class.ingredients())
 
diff --git a/recipe_scrapers/tests/test_whatsgabycooking.py b/recipe_scrapers/tests/test_whatsgabycooking.py
index 7d64b086..8164e6d7 100644
--- a/recipe_scrapers/tests/test_whatsgabycooking.py
+++ b/recipe_scrapers/tests/test_whatsgabycooking.py
@@ -8,9 +8,7 @@ class TestWhatsGabyCookingScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'whatsgabycooking.testhtml'
         )) as file_opened:
diff --git a/recipe_scrapers/tests/test_yummly.py b/recipe_scrapers/tests/test_yummly.py
index e80ff76f..58a4379c 100644
--- a/recipe_scrapers/tests/test_yummly.py
+++ b/recipe_scrapers/tests/test_yummly.py
@@ -8,9 +8,7 @@ class TestYummlyScraper(unittest.TestCase):
     def setUp(self):
         # tests are run from tests.py
         with open(os.path.join(
-            os.getcwd(),
-            'recipe_scrapers',
-            'tests',
+            os.path.dirname(os.path.realpath(__file__)),
             'test_data',
             'yummly.testhtml'
         )) as file_opened:
@@ -34,6 +32,12 @@ class TestYummlyScraper(unittest.TestCase):
             self.harvester_class.total_time()
         )
 
+    def test_yields(self):
+        self.assertEqual(
+            "4 serving(s)",
+            self.harvester_class.yields()
+        )
+
     def test_ingredients(self):
         self.assertSetEqual(
             set([