Advertisement

header ads

An Introduction to Mocking in Python

How to Run Unit Tests Without Testing Your Patience

More often than not, the software we write directly interacts with what we would label as “dirty” services. In layman’s terms: services that are crucial to our application, but whose interactions have intended but undesired side-effects—that is, undesired in the context of an autonomous test run.

For example: perhaps we’re writing a social app and want to test out our new ‘Post to Facebook feature’, but don’t want to actually post to Facebook every time we run our test suite.

The Python unittest library includes a subpackage named unittest.mock—or if you declare it as a dependency, simply mock—which provides extremely powerful and useful means by which to mock and stub out these undesired side-effects.

Source | http://www.toptal.com/python/an-introduction-to-mocking-in-python

Note: mock is newly included in the standard library as of Python 3.3; prior distributions will have to use the Mock library downloadable via PyPI.

Fear System Calls

To give you another example, and one that we’ll run with for the rest of the article, consider system calls. It’s not difficult to see that these are prime candidates for mocking: whether you’re writing a script to eject a CD drive, a web server which removes antiquated cache files from /tmp, or a socket server which binds to a TCP port, these calls all feature undesired side-effects in the context of your unit-tests.

As a developer, you care more that your library successfully called the system function for ejecting a CD as opposed to experiencing your CD tray open every time a test is run.

As a developer, you care more that your library successfully called the system function for ejecting a CD (with the correct arguments, etc.) as opposed to actually experiencing your CD tray open every time a test is run. (Or worse, multiple times, as multiple tests reference the eject code during a single unit-test run!)

Likewise, keeping your unit-tests efficient and performant means keeping as much “slow code” out of the automated test runs, namely filesystem and network access.

For our first example, we’ll refactor a standard Python test case from original form to one using mock. We’ll demonstrate how writing a test case with mocks will make our tests smarter, faster, and able to reveal more about how the software works.

A Simple Delete Function 

We all need to delete files from our filesystem from time to time, so let’s write a function in Python which will make it a bit easier for our scripts to do so.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os

def rm(filename):
    os.remove(filename)


Obviously, our rm method at this point in time doesn’t provide much more than the underlying os.remove method, but our codebase will improve, allowing us to add more functionality here.

Let’s write a traditional test case, i.e., without mocks:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import rm

import os.path
import tempfile
import unittest

class RmTestCase(unittest.TestCase):

    tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")

    def setUp(self):
        with open(self.tmpfilepath, "wb") as f:
            f.write("Delete me!")
        
    def test_rm(self):
        # remove the file
        rm(self.tmpfilepath)
        # test that it was actually removed
        self.assertFalse(os.path.isfile(self.tmpfilepath), "Failed to remove the file.")
 
  
Our test case is pretty simple, but every time it is run, a temporary file is created and then deleted. Additionally, we have no way of testing whether our rm method properly passes the argument down to the os.remove call. We can assume that it does based on the test above, but much is left to be desired.

Refactoring with Mocks

Let’s refactor our test case using mock:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import rm

import mock
import unittest

class RmTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")
 
 
 
 
With these refactors, we have fundamentally changed the way that the test operates. Now, we have an insider, an object we can use to verify the functionality of another.

Potential Pitfalls

One of the first things that should stick out is that we’re using the mock.patch method decorator to mock an object located at mymodule.os, and injecting that mock into our test case method. Wouldn’t it make more sense to just mock os itself, rather than the reference to it at mymodule.os?

Well, Python is somewhat of a sneaky snake when it comes to imports and managing modules. At runtime, the mymodule module has its own os which is imported into its own local scope in the module. Thus, if we mock os, we won’t see the effects of the mock in the mymodule module.

The mantra to keep repeating is this:

 Mock an item where it is used, not where it came from.


If you need to mock the tempfile module for myproject.app.MyElaborateClass, you probably need to apply the mock to myproject.app.tempfile, as each module keeps its own imports.

With that pitfall out of the way, let’s keep mocking.

Adding Validation to ‘rm’

 The rm method defined earlier is quite oversimplified. We’d like to have it validate that a path exists and is a file before just blindly attempting to remove it. Let’s refactor rm to be a bit smarter:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

Great. Now, let’s adjust our test case to keep coverage up.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import rm

import mock
import unittest

class RmTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os, mock_path):
        # set up the mock
        mock_path.isfile.return_value = False
        
        rm("any path")
        
        # test that the remove call was NOT called.
        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
        
        # make the file 'exist'
        mock_path.isfile.return_value = True
        
        rm("any path")
        
        mock_os.remove.assert_called_with("any path")

Our testing paradigm has completely changed. We now can verify and validate internal functionality of methods without any side-effects.

File-Removal as a Service

So far, we’ve only been working with supplying mocks for functions, but not for methods on objects or cases where mocking is necessary for sending parameters. Let’s cover object methods first.

We’ll begin with a refactor of the rm method into a service class. There really isn’t a justifiable need, per se, to encapsulate such a simple function into an object, but it will at the very least help us demonstrate key concepts in mock. Let’s refactor:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path

class RemovalService(object):
    """A service for removing objects from the filesystem."""

    def rm(filename):
        if os.path.isfile(filename):
            os.remove(filename)

 You’ll notice that not much has changed in our test case:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import RemovalService

import mock
import unittest

class RemovalServiceTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os, mock_path):
        # instantiate our service
        reference = RemovalService()
        
        # set up the mock
        mock_path.isfile.return_value = False
        
        reference.rm("any path")
        
        # test that the remove call was NOT called.
        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
        
        # make the file 'exist'
        mock_path.isfile.return_value = True
        
        reference.rm("any path")
        
        mock_os.remove.assert_called_with("any path")

Great, so we now know that the RemovalService works as planned. Let’s create another service which declares it as a dependency:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path

class RemovalService(object):
    """A service for removing objects from the filesystem."""

    def rm(self, filename):
        if os.path.isfile(filename):
            os.remove(filename)
            

class UploadService(object):

    def __init__(self, removal_service):
        self.removal_service = removal_service
        
    def upload_complete(self, filename):
        self.removal_service.rm(filename)
 
  
Since we already have test coverage on the RemovalService, we’re not going to validate internal functionality of the rm method in our tests of UploadService. Rather, we’ll simply test (without side-effects, of course) that UploadService calls the RemovalService.rm method, which we know “just works™” from our previous test case.

There are two ways to go about this:
  1. Mock out the RemovalService.rm method itself.
  2. Supply a mocked instance in the constructor of UploadService.
As both methods are often important in unit-testing, we’ll review both.

Option 1: Mocking Instance Methods

The mock library has a special method decorator for mocking object instance methods and properties, the @mock.patch.object decorator:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import RemovalService, UploadService

import mock
import unittest

class RemovalServiceTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os, mock_path):
        # instantiate our service
        reference = RemovalService()
        
        # set up the mock
        mock_path.isfile.return_value = False
        
        reference.rm("any path")
        
        # test that the remove call was NOT called.
        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
        
        # make the file 'exist'
        mock_path.isfile.return_value = True
        
        reference.rm("any path")
        
        mock_os.remove.assert_called_with("any path")
      
      
class UploadServiceTestCase(unittest.TestCase):

    @mock.patch.object(RemovalService, 'rm')
    def test_upload_complete(self, mock_rm):
        # build our dependencies
        removal_service = RemovalService()
        reference = UploadService(removal_service)
        
        # call upload_complete, which should, in turn, call `rm`:
        reference.upload_complete("my uploaded file")
        
        # check that it called the rm method of any RemovalService
        mock_rm.assert_called_with("my uploaded file")
        
        # check that it called the rm method of _our_ removal_service
        removal_service.rm.assert_called_with("my uploaded file")

Great! We’ve validated that the UploadService successfully calls our instance’s rm method. Notice anything interesting in there? The patching mechanism actually replaced the rm method of all RemovalService instances in our test method. That means that we can actually inspect the instances themselves. If you want to see more, try dropping in a breakpoint in your mocking code to get a good feel for how the patching mechanism works.

Pitfall: Decorator Order

When using multiple decorators on your test methods, order is important, and it’s kind of confusing. Basically, when mapping decorators to method parameters, work backwards. Consider this example:

    @mock.patch('mymodule.sys')
    @mock.patch('mymodule.os')
    @mock.patch('mymodule.os.path')
    def test_something(self, mock_os_path, mock_os, mock_sys):
        pass

Notice how our parameters are matched to the reverse order of the decorators? That’s partly because of the way that Python works. With multiple method decorators, here’s the order of execution in pseudocode:

patch_sys(patch_os(patch_os_path(test_something)))

Since the patch to sys is the outermost patch, it will be executed last, making it the last parameter in the actual test method arguments. Take note of this well and use a debugger when running your tests to make sure that the right parameters are being injected in the right order.

Option 2: Creating Mock Instances

Instead of mocking the specific instance method, we could instead just supply a mocked instance to UploadService with its constructor. I prefer option 1 above, as it’s a lot more precise, but there are many cases where option 2 might be efficient or necessary. Let’s refactor our test again:


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from mymodule import RemovalService, UploadService

import mock
import unittest

class RemovalServiceTestCase(unittest.TestCase):
    
    @mock.patch('mymodule.os.path')
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os, mock_path):
        # instantiate our service
        reference = RemovalService()
        
        # set up the mock
        mock_path.isfile.return_value = False
        
        reference.rm("any path")
        
        # test that the remove call was NOT called.
        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
        
        # make the file 'exist'
        mock_path.isfile.return_value = True
        
        reference.rm("any path")
        
        mock_os.remove.assert_called_with("any path")
      
      
class UploadServiceTestCase(unittest.TestCase):

    def test_upload_complete(self, mock_rm):
        # build our dependencies
        mock_removal_service = mock.create_autospec(RemovalService)
        reference = UploadService(mock_removal_service)
        
        # call upload_complete, which should, in turn, call `rm`:
        reference.upload_complete("my uploaded file")
        
        # test that it called the rm method
        mock_removal_service.rm.assert_called_with("my uploaded file")

In this example, we haven’t even had to patch any functionality, we simply create an auto-spec for the RemovalService class, and then inject this instance into our UploadService to validate the functionality.

The mock.create_autospec method creates a functionally equivalent instance to the provided class. What this means, practically speaking, is that when the returned instance is interacted with, it will raise exceptions if used in illegal ways. More specifically, if a method is called with the wrong number of arguments, an exception will be raised. This is extremely important as refactors happen. As a library changes, tests break and that is expected. Without using an auto-spec, our tests will still pass even though the underlying implementation is broken.

Pitfall: The mock.Mock and mock.MagicMock Classes

The mock library also includes two important classes upon which most of the internal functionality is built upon: [mock.Mock](http://www.voidspace.org.uk/python/mock/mock.html) and mock.MagicMock. When given a choice to use a mock.Mock instance, a mock.MagicMock instance, or an auto-spec, always favor using an auto-spec, as it helps keep your tests sane for future changes. This is because mock.Mock and mock.MagicMock accept all method calls and property assignments regardless of the underlying API. Consider the following use case:

class Target(object):
    def apply(value):
        return value

def method(target, value):
    return target.apply(value)

We can test this with a mock.Mock instance like this:

class MethodTestCase(unittest.TestCase):

    def test_method(self):
        target = mock.Mock()

        method(target, "value")

        target.apply.assert_called_with("value")

This logic seems sane, but let’s modify the Target.apply method to take more parameters:

class Target(object):
    def apply(value, are_you_sure):
        if are_you_sure:
            return value
        else:
            return None

Re-run your test, and you’ll find that it still passes. That’s because it isn’t built against your actual API. This is why you should always use the create_autospec method and the autospec parameter with the @patch and @patch.object decorators.

Real-World Example: Mocking a Facebook API Call

 To finish up, let’s write a more applicable real-world example, one which we mentioned in the introduction: posting a message to Facebook. We’ll write a nice wrapper class and a corresponding test case.

import facebook

class SimpleFacebook(object):
    
    def __init__(self, oauth_token):
        self.graph = facebook.GraphAPI(oauth_token)

    def post_message(self, message):
        """Posts a message to the Facebook wall."""
        self.graph.put_object("me", "feed", message=message)

Here’s our test case, which checks that we post the message without actually posting the message:

import facebook
import simple_facebook
import mock
import unittest

class SimpleFacebookTestCase(unittest.TestCase):
    
    @mock.patch.object(facebook.GraphAPI, 'put_object', autospec=True)
    def test_post_message(self, mock_put_object):
        sf = simple_facebook.SimpleFacebook("fake oauth token")
        sf.post_message("Hello World!")

        # verify
        mock_put_object.assert_called_with(message="Hello World!")

As we’ve seen so far, it’s really simple to start writing smarter tests with mock in Python.

Conclusion

Python’s mock library, if a little confusing to work with, is a game-changer for unit-testing. We’ve demonstrated common use-cases for getting started using mock in unit-testing, and hopefully this article will help Python developers overcome the initial hurdles and write excellent, tested code.


For more information visit | www.toptal.com



Post a Comment

67 Comments

  1. I have been searching for this similar kind of post about dot net developers for the past week and hardly came across this. Thank you very much, and I will look for more postings from you. It offers incredible information. I really need to be much obliged for this great blog.

    ReplyDelete
  2. Fantasy world777 cricket is extremely popular among fans and aficionados because it allows them to put their cricket expertise to the test. In addition, the popularity of fantasy cricket tournaments has resulted in the invention of new ways for users to make money.

    ReplyDelete
  3. This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Keep it up. You might also like adidas product tester

    ReplyDelete
  4. Incorporating https://tapczany.info/retro items into a current space can yield a distinctive and individual appearance. Here are a few suggestions for accomplishing this effectively.Firstly, don’t be afraid to blend styles. Even though a few vintage pieces may appear odd in a current home, their contrast can in fact add interest and character.Second, consider the proportion of the pieces. Oversized vintage pieces can overwhelm a area, while more compact ones can get lost in a large room.Thirdly, consider refurbishing or revamping vintage pieces to give them a new appearance. This can help them fit more easily with your contemporary decor.

    ReplyDelete
  5. Introductions. The writer's title is actually Linda Cooley MD. I am a customer service representative. To do ceramics is something her partner does not truly like but she does. My other half and I reside in Guam. I'm bad at webdesign however you might wish to examine my site: Check piusxiipope.info/o-nas

    ReplyDelete
  6. Болдырева Екатерина (36 лучших фото) HD фото https://cojo.ru/znamenitosti/boldyreva-ekaterina-36-foto/

    ReplyDelete
  7. Наша сеть востребованных online магазинов телевизоры самсунг 4k Архангельск предлагает не только лишь приобрести выпускаемую продукцию, а также обрести широкий набор услуг. Мы также предлагаем гарантийное и послегарантийное обслуживание, что позволяет вам лично быть спокойными в прочности и надежности вашей покупки. Запросто можете полностью доверять нашим сервисным центрам, именно там эксперты запросто устранят любые неполадки и могут помочь вам с использованием вашего девайса.

    ReplyDelete
  8. К тому же, мы знаем, что в свою очередь ваше время ценно, поэтому все наши магазины купить телевизоры samsung 8k Ухта обеспечивают комфортное и быстрое документальное оформление товаров. Мы ценим ваш комфорт и пытаемся спроектировать покупку простой и приятной.Можно выбрать товар, который вам необходим, заказать и далее оплатить его, не расходуя много вашего времени.

    ReplyDelete
  9. Балаяж на темные короткие прямые волосы милые картинки https://cojo.ru/pricheski-i-strizhki/balayazh-na-temnye-korotkie-pryamye-volosy-44-foto/

    ReplyDelete
  10. Hi, I am actually Colson and also I'm an auto enthusiast. I really love everything about automobiles, coming from their style as well as functionality to their history and also society. I operate as an auto mechanics and I enjoy restoring old cars in my downtime. I reside in Togo, where I can admire many different kinds of automobiles on the streets. I joined this online forum to discuss my passion and learn from other cars and truck enthusiasts. My website: explore and inspire welcome to our passionate blog community

    ReplyDelete
  11. Можно задать абсолютно любые имеющиеся вопросы, те что связаны с техническими параметрами функциональностью, и положительными качествами каждого продукта. Наши специалисты наделены полными познаниями о продукции Samsung смарт часы самсунг galaxy watch и также всегда рады поделиться с вами востребованной информацией.Практически во всех наши магазинах серьезный персонал, всегда готовый оказать помощь и консультацию, выбрать и приобретению нужного вам устройства.

    ReplyDelete
  12. Джасинда Барретт 33 фото лучшие картинки https://cojo.ru/

    ReplyDelete
  13. My name is Delia Cortez, and I live in Central African Republic. My hobby is Baseball. Website: Możliwe, że to najlepsza zabawka na świecie

    ReplyDelete
  14. Аватарки для youtube канала лучшие картинки https://fotoslava.ru/avatarki-dlya-youtube-kanala

    ReplyDelete
  15. My name is Faye Hines, and I live in a charming town by the lake in Norfolk Island. Wrestling is my favorite hobby, and you can find more about me on my website, https://kamalalmolk.info/1_top-dog-groomers-in-dublin-ireland-pamper-your-pooch-with-the-best-care.html

    ReplyDelete
  16. Прически ниже плеч (44 фото) UHD https://byry.ru/pricheski-nizhe-plech/

    ReplyDelete
  17. Charlotte Reese, a 41-year-old enthusiast of Fencing, complete of electricity and enthusiasm, staying in the vibrant city of Zimbabwe. Site: nie popelnij tych bledow szukajac opiekunki dla swojego dziecka

    ReplyDelete
  18. Восточный хвост (49 фото) - лучшие фото идеи с названиями красивые фото https://fotoslava.ru/vostochnyy-hvost

    ReplyDelete
  19. I am Bobbie Mcguire, I stay in New Caledonia and also I love to play Mixed Martial Arts. I welcome you to visit my web site: take on an adventure alemanne

    ReplyDelete
  20. Sweetberries4 классные фото https://fotoslava.ru/sweetberries4

    ReplyDelete
  21. Царговые двери в интерьере (91 фото) смотреть фото https://fotoslava.ru/czargovye-dveri-v-interere-91-foto

    ReplyDelete
  22. My name is Guadalupe Reeves and I live in Algeria. I am 21 years old and my hobby is Horse Racing. Sponsor melodic shades of the blizzard www.in-trance.info

    ReplyDelete
  23. Кухня гостиная в европейском стиле (32 фото) HD фото https://fotoslava.ru/kuhnya-gostinaya-v-evropejskom-stile-32-foto

    ReplyDelete
  24. Бель Дельфин лучшие картинки https://fotoslava.ru/bel-delfin

    ReplyDelete
  25. Rogelio Padilla, 42-letni rezydent Kuwejt, istnieję nielichym maniakiem Bobsleje, jaki notorycznie przeznacza trwanie na obłudę również rozmieszczanie tutejszych finezje. Zapraszam do odwiedzenia mojej ściany: pomysl na prezent obrazki dla dzieci

    ReplyDelete
  26. Samsung A30 Wallpapers HIGH QUALITY absolutely free https://wallpapershigh.com/samsung-a30

    ReplyDelete
  27. Virat Kohli In Rcb Jersey Wallpapers high resolution 100% free https://wallpapershigh.com/virat-kohli-in-rcb-jersey

    ReplyDelete
  28. Jestem Tymoteusz Ortiz, 25-letni nawiedzony Narciarstwo egzystujący w Kirgistan, tudzież moją perspektywą elektroniczną egzystuje jak znalezc rodzine przez internet zrodzina.pl

    ReplyDelete
  29. Laura Ashley Matching And Curtains Wallpapers high definition absolutely free https://wallpapershigh.com/laura-ashley-matching-and-curtains

    ReplyDelete
  30. Anjaneya God Wallpapers high definition absolutely free https://wallpapershigh.com/anjaneya-god

    ReplyDelete
  31. Rewerencja! Egzystuję Leah Garza, 24-letnia maniaczka Deskorolka spośród Gruzja. Niniejszy jogging nie owszem istnieje moje zamiłowaniach, ale wyjątkowo podpowiada na moją świadomość. Miejscowość: Bali All-Inclusive baladika.info

    ReplyDelete
  32. Camera Photography Wallpapers High Definition absolutely free https://wallpapershigh.com/camera-photography

    ReplyDelete
  33. Istnieję Judyta Lis, 23-latka żyjąca w Finlandia, przepadam odtwarzać w Podnoszenie ciężarów natomiast realizuję familiarną oryginalną okolicę komputerową: sklepzfotelikami.pl oferta

    ReplyDelete
  34. Hannah Nunn Wallpapers High Res 100% free https://wallpapershigh.com/hannah-nunn

    ReplyDelete
  35. I am Margarita Farmer, 34 years aged, staying in Chile, and my interest is actually Basketball. Browse through my internet site: liawrobexper1984.wordpress.com/2023/07/21/releasing-your-inner-warrior-exploring-the-excitement-of-action-games/

    ReplyDelete
  36. Heatheredeffect Wallpapers HIGH RES fast and free https://wallpapershigh.com/heatheredeffect

    ReplyDelete
  37. Egzystuję Natan Szewczyk, 26-letnim cholerykiem kuksańcaLacrosse, spędzającym w Mołdawia, natomiast moja właściwość multimedialna obecne wkrótce: https://www.openstreetmap.org/user/mariafont6

    ReplyDelete
  38. Inuyasha And Kagome Wallpapers Fullhd 100% free https://wallpapershigh.com/inuyasha-and-kagome

    ReplyDelete
  39. Off White Lockscreen Wallpapers FullHD 100% free https://wallpapershigh.com/off-white-lockscreen

    ReplyDelete
  40. Czesc, przytaczam się Oskar Jasiński ciągnę 37 latek również istnieję w Oman. Moje zainteresowaniami toteż Tenis. Sprawdz moją perspektywę: strona

    ReplyDelete
  41. Hitman Logo Wallpapers high resolution fast and free https://wallpapershigh.com/hitman-logo

    ReplyDelete
  42. Jestem Łukasz Brzeziński tworzę 28 latek. Jestem w Antigua i Barbuda a moje zbieractwo wówczas Bobsleje. Rozumiej: https://tofcoanuni1973.wordpress.com

    ReplyDelete
  43. Если ты хочешь взглянуть на великолепные фотографии из жизни талантливой боксерши, то рекомендую тебе заглянуть на страницу с галереей Натальи Рогозиной. На ней собрано 23 захватывающих фото, которые без сомнения потрясут твое воображение. Узреть мощь и изящество спортсменки в процессе тренировок и на ринге – это невероятное зрелище! Не упусти возможность окунуться в увлекательный мир бокса и насладиться искусством Натальи Рогозиной! Просто перейди по ссылке и наслаждайся этой потрясающей галереей фотографий! Перейти сюда https://cojo.ru/znamenitosti/natalya-rogozina-bokser-23-foto/

    ReplyDelete
  44. Reputacja. Spośród aktualnej kartki Kuba Gajewska kiwaj 45 lat tudzież lubie biegac. Stwierdź: https://linkhay.com/link/6718924/weebly-informations

    ReplyDelete
  45. Привет, фанаты картинок и фото! Если вы тоже чувствуете в себе необъяснимую привязанность к загадочным существам и хотите увидеть настоящие картинки чупакабра, то мне есть, что вам посоветовать! Я недавно наткнулся на галерею с 69 потрясающими фотографиями этих таинственных созданий. Уверен, вам будет интересно взглянуть на них! Чтобы увидеть эту удивительную коллекцию картинок чупакабра, просто перейдите по ссылке https://byry.ru/kartinki-chupakabra/. Готовы? Тогда давайте начнем увлекательное путешествие в мир чупакабра!

    ReplyDelete
  46. Я недавно покупал ДСП для пола на сайте fanwood.by и остался очень доволен! Хотелось найти недорогое решение, но без потери качества. Нашел всё, что нужно, на странице https://fanwood.by/shop/dsp-dvp-i-mdf/. Здесь есть широкий выбор покрытий по очень приемлемой цене. Качество материала отличное, подходит для разных целей и придаст полу стильный вид. Рекомендую заглянуть на сайт fanwood.by и купить ДСП для пола!

    ReplyDelete
  47. Ё, ребят! У меня для вас крутые картинки с артом эльфа крови чернокнижника! Не ожидал такой збс найти на этом сайте https://creofoto.ru/elf-krovi-chernoknizhnik-art. Там полно реально крутых работ, супер вдохновляет, руки чешутся такой же стафф нарисовать. Вобщем, чекайте этих эльфиков, окунитесь в их темный и магический мир! ������

    ReplyDelete
  48. Надумал вам показать несколько забавных картинок про субботку! Поверьте, ржач гарантирован! �� А вот еще одна крутая штука для любителей смеяться до слез - смотрите на эти дивные выдумки про субботу! Если хотите еще больше смешных штук по этой теме, то взгляните на эту ссылку: https://xwow.ru/rzhachnye-pro-subbotu Там вы найдете море смеха и отлично скоротаете время! ��

    ReplyDelete
  49. Ща дам супер рекомендацию! Если ты любишь Железного человека и хочешь крутые обои на свой компьютер или телефон, то обязательно зайди на сайт creofoto.ru! Там ты найдешь огромную коллекцию обоев с Железным человеком! Давай, переходи по ссылке https://creofoto.ru/zheleznyy-chelovek-oboi и выбирай свои самые классные обои! Будешь в восторге, поэтому обязательно загляни!

    ReplyDelete
  50. Картинки сюрприз могут поднять настроение и доставить радость! Если вы ищете коллекцию забавных и неожиданных изображений, то рекомендую посетить страницу https://cojo.ru/kartinki/kartinki-syurpriz-40-foto/. Там вы найдете 40 удивительных фото, которые точно заставят вас улыбнуться и позитивно настроиться на весь день! Разнообразные сюрпризы, забавные ситуации и необычные предметы - всё это есть на этой странице. Посетите ее и насладитесь прекрасными картинками!

    ReplyDelete
  51. Хочу рассказать о своем положительном опыте посещения Автосервиса АвтоКрасное в Гомеле. Когда у меня возникла проблема с заменой сцепления на моем автомобиле марки WAG, я обратился в этот автосервис. Искренне благодарен всем сотрудникам АвтоКрасное за их квалифицированную помощь и профессионализм. Ремонт был выполнен быстро и качественно. Я остался очень доволен результатом и рекомендую этот сервис всем владельцам WAG, которым необходима замена сцепления. Более подробную информацию о замене сцепления в автосервисе АвтоКрасное можно найти на их сайте по ссылке https://service-krasnoe.by/zamena-sczepleniya/.

    ReplyDelete
  52. Айя Садуакасова - талантливая актриса, чья карьера успешно развивается. Ее актерское мастерство, стиль и красота привлекают внимание зрителей и поклонников. На протяжении многих лет Айя снимается в различных проектах и демонстрирует свой профессионализм. Ее игра вливается в образы с такой убедительностью, что зрители не могут не поверить в то, что происходит на экране. Несомненно, Айя Садуакасова оставляет неизгладимое впечатление своими ролями и олицетворяет незабываемые персонажи. Если вы хотите узнать больше о творчестве и жизни этой замечательной актрисы, обязательно посетите ссылку https://cojo.ru/znamenitosti/ayya-saduakasova-aktrisa-39-foto/, где вы сможете насладиться 39 профессиональными фотографиями Айи Садуакасовой.

    ReplyDelete
  53. Заказать дизайн-проект трехкомнатной квартиры. Заказать популярный дизайн проект жилища в СПб. Создадим дизайн проект вашей жилплощади, продолжительность производства два-три календарных месяца. Три разновидности исполнения проекта. Запишись на безвозмездный поиск стиля дизайна для будущего бюджета. Расценки на услугу начинаются от 3998 руб/квадратный метр. Позвоните на наш номер по телефону 89150013100. Адрес: Рф, СПб. График рабочего дня: с 9ч до 19ч, суббота, вс - выходные дни. Представительство на удаленке, функционируем online

    ReplyDelete
  54. Привет! Меня зовут Анна, и я хочу поделиться с вами своим потрясающим опытом игры в интернет-казино Вавада. Сегодня я хочу рассказать о том, как с помощью Вавада и его зеркала на сегодня vavadacag1 я смогла выиграть крупную сумму в слотах. Удивительно, но после простой регистрации на https://vavadax4.site/ я получила не только приветственный бонус, но и фриспины! В интерфейсе Вавада все продумано до мелочей, что делает игру еще более удобной и захватывающей. Больше всего меня поразили быстрые выплаты - выигрыш моментально поступил на мой счет. Я очень довольна своим опытом в Вавада и рекомендую его всем любителям азартных игр. Попробуйте и вы, и у вас обязательно будет удача на вашей стороне!

    ReplyDelete
  55. Вчера я нашла замечательный шарф из коллекции Ализе - яркий, нежный и теплый! Он идеально дополнит любой образ и создаст неповторимый стиль. Качество материала превзошло все мои ожидания! Если вы хотите увидеть его своими глазами и выбрать модель по душе, я рекомендую перейти по ссылке https://fotofakt.ru/ochki-lui-vitton. Там вас ждет галерея фотографий на все вкусы! Не упустите возможность обновить свой гардероб с шарфом Ализе!

    ReplyDelete
  56. Фон бассейн – отличный выбор для создания эффектной атмосферы на вашем сайте или в дизайне. Бассейны с их сияющей водой и элегантными фигурами складываются в прекрасную гармонию с окружающей природой, а также добавляют особый шарм к отелям и спа-комплексам. На сайте https://sotni.ru/fon-basseyn/ вы найдете широкий выбор качественных фотографий бассейнов, которые помогут вам воплотить в жизнь самые смелые идеи в дизайне.

    ReplyDelete
  57. Привет, друзья! Хочу поделиться с вами своей недавней находкой - женским пальто комбинированного дизайна! Это просто волшебство - сочетание разных материалов и расцветок создают неповторимый образ. Я нашла галерею с такими пальто на сайте FotoFakt.ru, и уверена, что вам они тоже понравятся. Не стесняйтесь пройти по ссылке https://fotofakt.ru/natsionalnyy-kostyum-moldavii, и вы увидите все эти красоты своими глазами. Уверена, вы не останетесь равнодушными!

    ReplyDelete
  58. Меня зовут Алексей, и я хочу поделиться своим отличным опытом игры в интернет-казино Вавада. Я долго искал надежное и честное онлайн-казино, и наконец нашел то, что удовлетворило все мои требования. Вавада предлагает огромный выбор слотов от ведущих поставщиков программного обеспечения, а также различные бонусы и акции. Что меня особенно порадовало, так это быстрые выплаты. Однажды я выиграл приличную сумму и задался вопросом как вывести деньги на ваваде. Сайт предлагает разные способы вывода, в том числе и на банковскую карту. Регистрация в Вавада очень простая и занимает всего несколько минут. Интерфейс сайта очень удобный и интуитивно понятный, так что даже новичку будет легко разобраться. Кроме того, Вавада предлагает щедрый приветственный бонус и фриспины для новых игроков, что помогает увеличить шансы на крупный выигрыш. Я очень доволен своим опытом игры в Вавада и настоятельно рекомендую всем попробовать свою удачу на этом замечательном сайте! https://vavadah.site/.

    ReplyDelete
  59. Тут крутые зеленые фоны для фотошопа, особенно для детских фоток. Запрыгивай сюда и выбирай! Они добавят твоим фоткам сказочную обстановку.

    ReplyDelete
  60. Пасха - это один из главных православных праздников, который отмечается во всем мире. В этот день христиане отмечают Воскресение Иисуса Христа. Пасха ассоциируется с яркими красками, весенним настроением и радостью. Как и любой другой праздник, Пасха сопровождается традиционными символами и обычаями. Одним из них являются пасхальные картинки, которые могут быть различных видов: от росписи яиц до пасхальных козлят и зайцев. Если вы хотите украсить свой дом к празднику или просто насладиться красивыми картинками, то рекомендую вам посетить сайт https://cojo.ru/kartinki/pasha-kartinki-52-foto/. Там вы найдете 52 фотографии с пасхальными изображениями, которые точно вас вдохновят и добавят праздничного настроения.

    ReplyDelete
  61. Я играю в интернет-казино уже несколько лет и никогда еще не получал таких крупных выигрышей, как в Вавада! Рекомендую всем попробовать свою удачу в этом заведении. Вавада мобильная версия зеркало на сегодня vavadakri11 (ссылка вавада мобильная версия зеркало на сегодня vavadakri11) - отлично подходит для тех, кто предпочитает играть в слоты через мобильное устройство. В казино представлено огромное количество игровых автоматов, которые удивляют разнообразием и качеством графики. Кроме того, Вавада радует своих клиентов щедрыми бонусами и фриспинами, которые дают возможность выиграть еще больше. Регистрация на сайте простая и моментальная, а выплаты происходят быстро и без задержек. Интерфейс казино очень удобный, даже новичок сможет разобраться без проблем. А еще Вавада дарит приветственный бонус, который позволяет начать игру с большими суммами. Я очень доволен своим опытом игры в Вавада и советую всем испытать свою удачу в этом казино!

    ReplyDelete
  62. Привет, друзья! Хочу поделиться с вами своей недавней находкой – сказочными картинками и красивыми гифками. Они просто восхитительны! Как будто оживают прямо на наших глазах. Я нашел эту потрясающую подборку на сайте cojo.ru. Вот ссылка: https://cojo.ru/gifki/skazochnye-kartinki-krasivye-gifki/. Переходите по ней, и вы откроете для себя мир сказки и красоты. Эти картинки и гифки заставят вас улыбнуться и забыть о повседневных заботах. Насладитесь чудесными мгновениями и поделитесь ссылкой с друзьями. Уверен, им тоже понравится это волшебство!

    ReplyDelete
  63. Привет всем! Сегодня хотел бы порекомендовать вам замечательный способ поздравить женщину с днем рождения - гифки! Эти анимированные картинки отлично поднимут настроение и подарят улыбку имениннице. Если вы хотите найти множество разнообразных и оригинальных гифок для поздравления, рекомендую вам посетить страницу поздравить женщину с днем рождения гифки. Там вы найдете не только прекрасные и красочные анимации, но и возможность выбрать самую подходящую гифку и отправить ее в качестве поздравления. Пусть ваше поздравление станет еще более ярким и запоминающимся с помощью гифок! Желаю всем творческого настроения и радости!

    ReplyDelete
  64. Всем любителям кошек предлагаю посмотреть на действительно удивительных пушистых созданий - кошек с огромными ушами! Представляете, какие они милые и необычные! Их уши просто огромные, и это придает им такой особый шарм. Если вы хотите насладиться прекрасными фотографиями этих котиков, то я нашел для вас отличный ресурс - https://cojo.ru/koty/koshki-s-ogromnymi-ushami-27-foto/. Здесь вы найдете великолепные снимки и узнаете больше о таких интересных породах кошек. Не упустите возможность увидеть их своими глазами!

    ReplyDelete
  65. Продукция сменные кассеты gillette купить оптом, это отличное начало нового бизнеса. Постоянные распродажи на сменные картриджи джилет фьюжен проглайд. Средства для бритья триммер-лезвие fusion практичные наборы gillette купить оптом по минимальной стоимости производителя. Отличная возможность заказать лезвия gillette mach3, станки для бритья gillette mach3, а также любой другой продукт серии gillette mach 3 по максимальной выгодой цене!. Всегда в наличии популярные одноразовые станки gillette 2.

    ReplyDelete