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
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")
Potential Pitfalls
One of the first things that should stick out is that we’re using themock.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’
Therm
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")
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)
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:
- Mock out the
RemovalService.rm
method itself. - Supply a mocked instance in the constructor of
UploadService
.
Option 1: Mocking Instance Methods
Themock
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
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 toUploadService
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
80 Comments
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.
ReplyDeleteFantasy 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.
ReplyDeleteThis 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
ReplyDeleteIncorporating 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.
ReplyDeleteMahakali mataji wallpapers https://wallpapershigh.com/mahakali-mataji
ReplyDeleteIntroductions. 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Болдырева Екатерина (36 лучших фото) HD фото https://cojo.ru/znamenitosti/boldyreva-ekaterina-36-foto/
ReplyDeleteНаша сеть востребованных online магазинов телевизоры самсунг 4k Архангельск предлагает не только лишь приобрести выпускаемую продукцию, а также обрести широкий набор услуг. Мы также предлагаем гарантийное и послегарантийное обслуживание, что позволяет вам лично быть спокойными в прочности и надежности вашей покупки. Запросто можете полностью доверять нашим сервисным центрам, именно там эксперты запросто устранят любые неполадки и могут помочь вам с использованием вашего девайса.
ReplyDeleteК тому же, мы знаем, что в свою очередь ваше время ценно, поэтому все наши магазины купить телевизоры samsung 8k Ухта обеспечивают комфортное и быстрое документальное оформление товаров. Мы ценим ваш комфорт и пытаемся спроектировать покупку простой и приятной.Можно выбрать товар, который вам необходим, заказать и далее оплатить его, не расходуя много вашего времени.
ReplyDeleteБалаяж на темные короткие прямые волосы милые картинки https://cojo.ru/pricheski-i-strizhki/balayazh-na-temnye-korotkie-pryamye-volosy-44-foto/
ReplyDeleteHi, 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Можно задать абсолютно любые имеющиеся вопросы, те что связаны с техническими параметрами функциональностью, и положительными качествами каждого продукта. Наши специалисты наделены полными познаниями о продукции Samsung смарт часы самсунг galaxy watch и также всегда рады поделиться с вами востребованной информацией.Практически во всех наши магазинах серьезный персонал, всегда готовый оказать помощь и консультацию, выбрать и приобретению нужного вам устройства.
ReplyDeleteДжасинда Барретт 33 фото лучшие картинки https://cojo.ru/
ReplyDeleteMy 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Аватарки для youtube канала лучшие картинки https://fotoslava.ru/avatarki-dlya-youtube-kanala
ReplyDeleteMy 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Прически ниже плеч (44 фото) UHD https://byry.ru/pricheski-nizhe-plech/
ReplyDeleteCharlotte 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Восточный хвост (49 фото) - лучшие фото идеи с названиями красивые фото https://fotoslava.ru/vostochnyy-hvost
ReplyDeleteI 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
ReplyDeleteSweetberries4 классные фото https://fotoslava.ru/sweetberries4
ReplyDeleteЦарговые двери в интерьере (91 фото) смотреть фото https://fotoslava.ru/czargovye-dveri-v-interere-91-foto
ReplyDeleteMy 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Кухня гостиная в европейском стиле (32 фото) HD фото https://fotoslava.ru/kuhnya-gostinaya-v-evropejskom-stile-32-foto
ReplyDeleteБель Дельфин лучшие картинки https://fotoslava.ru/bel-delfin
ReplyDeleteRogelio 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
ReplyDeleteSamsung A30 Wallpapers HIGH QUALITY absolutely free https://wallpapershigh.com/samsung-a30
ReplyDeleteVirat Kohli In Rcb Jersey Wallpapers high resolution 100% free https://wallpapershigh.com/virat-kohli-in-rcb-jersey
ReplyDeleteJestem Tymoteusz Ortiz, 25-letni nawiedzony Narciarstwo egzystujący w Kirgistan, tudzież moją perspektywą elektroniczną egzystuje jak znalezc rodzine przez internet zrodzina.pl
ReplyDeleteLaura Ashley Matching And Curtains Wallpapers high definition absolutely free https://wallpapershigh.com/laura-ashley-matching-and-curtains
ReplyDeleteAnjaneya God Wallpapers high definition absolutely free https://wallpapershigh.com/anjaneya-god
ReplyDeleteRewerencja! 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
ReplyDeleteCamera Photography Wallpapers High Definition absolutely free https://wallpapershigh.com/camera-photography
ReplyDeleteIstnieję Judyta Lis, 23-latka żyjąca w Finlandia, przepadam odtwarzać w Podnoszenie ciężarów natomiast realizuję familiarną oryginalną okolicę komputerową: sklepzfotelikami.pl oferta
ReplyDeleteHannah Nunn Wallpapers High Res 100% free https://wallpapershigh.com/hannah-nunn
ReplyDeleteI 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/
ReplyDeleteHeatheredeffect Wallpapers HIGH RES fast and free https://wallpapershigh.com/heatheredeffect
ReplyDeleteEgzystuję 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
ReplyDeleteInuyasha And Kagome Wallpapers Fullhd 100% free https://wallpapershigh.com/inuyasha-and-kagome
ReplyDeleteOff White Lockscreen Wallpapers FullHD 100% free https://wallpapershigh.com/off-white-lockscreen
ReplyDeleteCzesc, przytaczam się Oskar Jasiński ciągnę 37 latek również istnieję w Oman. Moje zainteresowaniami toteż Tenis. Sprawdz moją perspektywę: strona
ReplyDeleteHitman Logo Wallpapers high resolution fast and free https://wallpapershigh.com/hitman-logo
ReplyDeleteJestem Łukasz Brzeziński tworzę 28 latek. Jestem w Antigua i Barbuda a moje zbieractwo wówczas Bobsleje. Rozumiej: https://tofcoanuni1973.wordpress.com
ReplyDeleteЕсли ты хочешь взглянуть на великолепные фотографии из жизни талантливой боксерши, то рекомендую тебе заглянуть на страницу с галереей Натальи Рогозиной. На ней собрано 23 захватывающих фото, которые без сомнения потрясут твое воображение. Узреть мощь и изящество спортсменки в процессе тренировок и на ринге – это невероятное зрелище! Не упусти возможность окунуться в увлекательный мир бокса и насладиться искусством Натальи Рогозиной! Просто перейди по ссылке и наслаждайся этой потрясающей галереей фотографий! Перейти сюда https://cojo.ru/znamenitosti/natalya-rogozina-bokser-23-foto/
ReplyDeleteReputacja. Spośród aktualnej kartki Kuba Gajewska kiwaj 45 lat tudzież lubie biegac. Stwierdź: https://linkhay.com/link/6718924/weebly-informations
ReplyDeleteFaktycznie płynnie zarejestruj aktualne: telegra.ph/Releasing-Fun-Exploring-the-Captivating-World-of-Games-08-20
ReplyDeleteПривет, фанаты картинок и фото! Если вы тоже чувствуете в себе необъяснимую привязанность к загадочным существам и хотите увидеть настоящие картинки чупакабра, то мне есть, что вам посоветовать! Я недавно наткнулся на галерею с 69 потрясающими фотографиями этих таинственных созданий. Уверен, вам будет интересно взглянуть на них! Чтобы увидеть эту удивительную коллекцию картинок чупакабра, просто перейдите по ссылке https://byry.ru/kartinki-chupakabra/. Готовы? Тогда давайте начнем увлекательное путешествие в мир чупакабра!
ReplyDeleteЯ недавно покупал ДСП для пола на сайте fanwood.by и остался очень доволен! Хотелось найти недорогое решение, но без потери качества. Нашел всё, что нужно, на странице https://fanwood.by/shop/dsp-dvp-i-mdf/. Здесь есть широкий выбор покрытий по очень приемлемой цене. Качество материала отличное, подходит для разных целей и придаст полу стильный вид. Рекомендую заглянуть на сайт fanwood.by и купить ДСП для пола!
ReplyDeleteЁ, ребят! У меня для вас крутые картинки с артом эльфа крови чернокнижника! Не ожидал такой збс найти на этом сайте https://creofoto.ru/elf-krovi-chernoknizhnik-art. Там полно реально крутых работ, супер вдохновляет, руки чешутся такой же стафф нарисовать. Вобщем, чекайте этих эльфиков, окунитесь в их темный и магический мир! ������
ReplyDeleteНадумал вам показать несколько забавных картинок про субботку! Поверьте, ржач гарантирован! �� А вот еще одна крутая штука для любителей смеяться до слез - смотрите на эти дивные выдумки про субботу! Если хотите еще больше смешных штук по этой теме, то взгляните на эту ссылку: https://xwow.ru/rzhachnye-pro-subbotu Там вы найдете море смеха и отлично скоротаете время! ��
ReplyDeleteЩа дам супер рекомендацию! Если ты любишь Железного человека и хочешь крутые обои на свой компьютер или телефон, то обязательно зайди на сайт creofoto.ru! Там ты найдешь огромную коллекцию обоев с Железным человеком! Давай, переходи по ссылке https://creofoto.ru/zheleznyy-chelovek-oboi и выбирай свои самые классные обои! Будешь в восторге, поэтому обязательно загляни!
ReplyDeleteКартинки сюрприз могут поднять настроение и доставить радость! Если вы ищете коллекцию забавных и неожиданных изображений, то рекомендую посетить страницу https://cojo.ru/kartinki/kartinki-syurpriz-40-foto/. Там вы найдете 40 удивительных фото, которые точно заставят вас улыбнуться и позитивно настроиться на весь день! Разнообразные сюрпризы, забавные ситуации и необычные предметы - всё это есть на этой странице. Посетите ее и насладитесь прекрасными картинками!
ReplyDeleteХочу рассказать о своем положительном опыте посещения Автосервиса АвтоКрасное в Гомеле. Когда у меня возникла проблема с заменой сцепления на моем автомобиле марки WAG, я обратился в этот автосервис. Искренне благодарен всем сотрудникам АвтоКрасное за их квалифицированную помощь и профессионализм. Ремонт был выполнен быстро и качественно. Я остался очень доволен результатом и рекомендую этот сервис всем владельцам WAG, которым необходима замена сцепления. Более подробную информацию о замене сцепления в автосервисе АвтоКрасное можно найти на их сайте по ссылке https://service-krasnoe.by/zamena-sczepleniya/.
ReplyDeleteАйя Садуакасова - талантливая актриса, чья карьера успешно развивается. Ее актерское мастерство, стиль и красота привлекают внимание зрителей и поклонников. На протяжении многих лет Айя снимается в различных проектах и демонстрирует свой профессионализм. Ее игра вливается в образы с такой убедительностью, что зрители не могут не поверить в то, что происходит на экране. Несомненно, Айя Садуакасова оставляет неизгладимое впечатление своими ролями и олицетворяет незабываемые персонажи. Если вы хотите узнать больше о творчестве и жизни этой замечательной актрисы, обязательно посетите ссылку https://cojo.ru/znamenitosti/ayya-saduakasova-aktrisa-39-foto/, где вы сможете насладиться 39 профессиональными фотографиями Айи Садуакасовой.
ReplyDeleteЗаказать дизайн-проект трехкомнатной квартиры. Заказать популярный дизайн проект жилища в СПб. Создадим дизайн проект вашей жилплощади, продолжительность производства два-три календарных месяца. Три разновидности исполнения проекта. Запишись на безвозмездный поиск стиля дизайна для будущего бюджета. Расценки на услугу начинаются от 3998 руб/квадратный метр. Позвоните на наш номер по телефону 89150013100. Адрес: Рф, СПб. График рабочего дня: с 9ч до 19ч, суббота, вс - выходные дни. Представительство на удаленке, функционируем online
ReplyDeleteПривет! Меня зовут Анна, и я хочу поделиться с вами своим потрясающим опытом игры в интернет-казино Вавада. Сегодня я хочу рассказать о том, как с помощью Вавада и его зеркала на сегодня vavadacag1 я смогла выиграть крупную сумму в слотах. Удивительно, но после простой регистрации на https://vavadax4.site/ я получила не только приветственный бонус, но и фриспины! В интерфейсе Вавада все продумано до мелочей, что делает игру еще более удобной и захватывающей. Больше всего меня поразили быстрые выплаты - выигрыш моментально поступил на мой счет. Я очень довольна своим опытом в Вавада и рекомендую его всем любителям азартных игр. Попробуйте и вы, и у вас обязательно будет удача на вашей стороне!
ReplyDeleteВчера я нашла замечательный шарф из коллекции Ализе - яркий, нежный и теплый! Он идеально дополнит любой образ и создаст неповторимый стиль. Качество материала превзошло все мои ожидания! Если вы хотите увидеть его своими глазами и выбрать модель по душе, я рекомендую перейти по ссылке https://fotofakt.ru/ochki-lui-vitton. Там вас ждет галерея фотографий на все вкусы! Не упустите возможность обновить свой гардероб с шарфом Ализе!
ReplyDeleteПривет, друзья! Хочу поделиться с вами своей недавней находкой - женским пальто комбинированного дизайна! Это просто волшебство - сочетание разных материалов и расцветок создают неповторимый образ. Я нашла галерею с такими пальто на сайте FotoFakt.ru, и уверена, что вам они тоже понравятся. Не стесняйтесь пройти по ссылке https://fotofakt.ru/natsionalnyy-kostyum-moldavii, и вы увидите все эти красоты своими глазами. Уверена, вы не останетесь равнодушными!
ReplyDeleteМеня зовут Алексей, и я хочу поделиться своим отличным опытом игры в интернет-казино Вавада. Я долго искал надежное и честное онлайн-казино, и наконец нашел то, что удовлетворило все мои требования. Вавада предлагает огромный выбор слотов от ведущих поставщиков программного обеспечения, а также различные бонусы и акции. Что меня особенно порадовало, так это быстрые выплаты. Однажды я выиграл приличную сумму и задался вопросом как вывести деньги на ваваде. Сайт предлагает разные способы вывода, в том числе и на банковскую карту. Регистрация в Вавада очень простая и занимает всего несколько минут. Интерфейс сайта очень удобный и интуитивно понятный, так что даже новичку будет легко разобраться. Кроме того, Вавада предлагает щедрый приветственный бонус и фриспины для новых игроков, что помогает увеличить шансы на крупный выигрыш. Я очень доволен своим опытом игры в Вавада и настоятельно рекомендую всем попробовать свою удачу на этом замечательном сайте! https://vavadah.site/.
ReplyDeleteТут крутые зеленые фоны для фотошопа, особенно для детских фоток. Запрыгивай сюда и выбирай! Они добавят твоим фоткам сказочную обстановку.
ReplyDeleteПасха - это один из главных православных праздников, который отмечается во всем мире. В этот день христиане отмечают Воскресение Иисуса Христа. Пасха ассоциируется с яркими красками, весенним настроением и радостью. Как и любой другой праздник, Пасха сопровождается традиционными символами и обычаями. Одним из них являются пасхальные картинки, которые могут быть различных видов: от росписи яиц до пасхальных козлят и зайцев. Если вы хотите украсить свой дом к празднику или просто насладиться красивыми картинками, то рекомендую вам посетить сайт https://cojo.ru/kartinki/pasha-kartinki-52-foto/. Там вы найдете 52 фотографии с пасхальными изображениями, которые точно вас вдохновят и добавят праздничного настроения.
ReplyDeleteЯ играю в интернет-казино уже несколько лет и никогда еще не получал таких крупных выигрышей, как в Вавада! Рекомендую всем попробовать свою удачу в этом заведении. Вавада мобильная версия зеркало на сегодня vavadakri11 (ссылка вавада мобильная версия зеркало на сегодня vavadakri11) - отлично подходит для тех, кто предпочитает играть в слоты через мобильное устройство. В казино представлено огромное количество игровых автоматов, которые удивляют разнообразием и качеством графики. Кроме того, Вавада радует своих клиентов щедрыми бонусами и фриспинами, которые дают возможность выиграть еще больше. Регистрация на сайте простая и моментальная, а выплаты происходят быстро и без задержек. Интерфейс казино очень удобный, даже новичок сможет разобраться без проблем. А еще Вавада дарит приветственный бонус, который позволяет начать игру с большими суммами. Я очень доволен своим опытом игры в Вавада и советую всем испытать свою удачу в этом казино!
ReplyDeleteПривет, друзья! Хочу поделиться с вами своей недавней находкой – сказочными картинками и красивыми гифками. Они просто восхитительны! Как будто оживают прямо на наших глазах. Я нашел эту потрясающую подборку на сайте cojo.ru. Вот ссылка: https://cojo.ru/gifki/skazochnye-kartinki-krasivye-gifki/. Переходите по ней, и вы откроете для себя мир сказки и красоты. Эти картинки и гифки заставят вас улыбнуться и забыть о повседневных заботах. Насладитесь чудесными мгновениями и поделитесь ссылкой с друзьями. Уверен, им тоже понравится это волшебство!
ReplyDeleteПривет всем! Сегодня хотел бы порекомендовать вам замечательный способ поздравить женщину с днем рождения - гифки! Эти анимированные картинки отлично поднимут настроение и подарят улыбку имениннице. Если вы хотите найти множество разнообразных и оригинальных гифок для поздравления, рекомендую вам посетить страницу поздравить женщину с днем рождения гифки. Там вы найдете не только прекрасные и красочные анимации, но и возможность выбрать самую подходящую гифку и отправить ее в качестве поздравления. Пусть ваше поздравление станет еще более ярким и запоминающимся с помощью гифок! Желаю всем творческого настроения и радости!
ReplyDeleteВсем любителям кошек предлагаю посмотреть на действительно удивительных пушистых созданий - кошек с огромными ушами! Представляете, какие они милые и необычные! Их уши просто огромные, и это придает им такой особый шарм. Если вы хотите насладиться прекрасными фотографиями этих котиков, то я нашел для вас отличный ресурс - https://cojo.ru/koty/koshki-s-ogromnymi-ushami-27-foto/. Здесь вы найдете великолепные снимки и узнаете больше о таких интересных породах кошек. Не упустите возможность увидеть их своими глазами!
ReplyDeleteПродукция сменные кассеты gillette купить оптом, это отличное начало нового бизнеса. Постоянные распродажи на сменные картриджи джилет фьюжен проглайд. Средства для бритья триммер-лезвие fusion практичные наборы gillette купить оптом по минимальной стоимости производителя. Отличная возможность заказать лезвия gillette mach3, станки для бритья gillette mach3, а также любой другой продукт серии gillette mach 3 по максимальной выгодой цене!. Всегда в наличии популярные одноразовые станки gillette 2.
ReplyDeleteПродукция съемные кассеты gillette купить оптом, это отличный способ начать свое дело. Постоянные скидки на лезвия джилет фьюжен проглайд. Средства для бритья лезвие fusion функциональные комплекты gillette купить оптом по оптимальной цене производителя. Отличная возможность купить лезвия gillette mach3, станки для бритья джилет мак 3 турбо, а также любой другой продукт линейки gillette mach 3 по максимальной выгодой стоимости!. Хит продаж одноразовые станки gillette 2.
ReplyDeleteЕсли вы хотите нарисовать дуб, но не знаете с чего начать, то предлагаю вам заглянуть на этот сайт https://sotni.ru/risunki-duba-dlya-srisovki/. Здесь вы найдете 71 фото с различными рисунками дуба, которые можно использовать для срисовки. Это отличный способ для начинающих художников получить больше навыков и вдохновения. Посмотрите удаленные ветви, мощный ствол и детали листьев на этих фотографиях, и вы сможете создать потрясающий рисунок дуба своими руками.
ReplyDeleteПродукция лезвия gillette купить оптом, это отличный способ начать свое дело. Постоянные скидки на сменные кассеты fusion proglide. Средства для бритья лезвие fusion стильные наборы gillette купить оптом по оптимальной цене производителя. Не упустите возможность заказать джилет мак 3 кассеты, станки для бритья джилет мак 3 турбо, а также любой другой продукт серии gillette mach3 по максимальной выгодой стоимости!. Хит продаж одноразовые станки для бритья gillette venus.
ReplyDeleteIsraFace - еврейский сайт знакомств, это блог о евреях, где находятся еврейки и евреи и русский еврей из США, Белоруссии. Показывайте какие хотите снимки, ролики, объединяйтесь в клуб, читайте блог, делайте визиты на форум, заводите еврейские знакомства.
ReplyDeleteПутешествия по России станут одним из лучших способов провести отпуск в этом году, погрузиться в культуру и историю страны, насладиться красотой ее природы и увидеть самые удивительные достопримечательности. отдых с детьми в России летом куда поехать Мы забронировали путевки для отдыха по России на ближайшие выходные. Планируйте свой отдых в России с турагентством по России. Подбор туров, экскурсий и горящих путевок по России.
ReplyDeleteПроизводитель товаров для бритья DIVIS PRO: Предлагает сотрудничество для региональных дилеров России! Наш ассортимент аксессуаров для бритья включает: Оригинальные сменные кассеты для бритья, бритвенные системы, одноразовые бритвы, станки и лезвия для бритья. Оригинальные сменные кассеты для бритья DIVIS PRO идеально подходят как для мужчин, так и для женщин. Совместимость со всеми станками для бритья Gillette. Сменные кассеты для бритья продаются в упаковках по 2, 4, 8 кассет для бритья. Покупая оригинальные сменные кассеты для бритья, бритвенные системы, одноразовые бритвы, станки и лезвия для бритья ДИВИС ПРО вы можете быть уверены, что предлагаете своим клиентам только оригинальный продукт. Начните сотрудничество с DIVIS PRO - производитель аксессуаров для бритья в качестве дистрибьютора в России и получите: Индивидуальные предложения. Производитель аксессуаров для бритья DIVIS PRO - вместе к новым высотам!
ReplyDeleteФундаментный блок является основой, на которой держится весь дом, поэтому его выбор и установка требуют особого внимания и профессионализма. Правильная техника вязки арматуры для ленточного фундамента Давайте рассмотрим типичные ошибки, которые совершают при возведении фундамента. Многие из них имеют крайне негативный эффект на дальнейшем строительстве террасы.
ReplyDeleteI highly recommend checking out the cuckold scene on this adult site. It offers a wide range of content for those interested in exploring this particular niche. My friend Sarah absolutely loves the selection and variety available, so I'm sure you'll enjoy it too!
ReplyDeleteWhole House Renovation Cost Calculator Canada house renovation pictures
ReplyDeleteЭй, чуваки! Сегодня решил поделиться с вами парочкой олдскульных фоточек, где девчонки показывают низ груди. А не супер ли это?�� Если хотите посмотреть еще больше интересных картинок, то обязательно проверьте ссылку https://subliminalkey.com/devushki-pokazyvayut-niz-grudi. Там прям очень много всего, что стоит обязательно посмотреть! Порадуйте свои глазки��
ReplyDeleteIsraFace - еврейский сайт знакомств, это сообщество евреев, где общаются еврейки и евреи и русский еврей из . Показывайте крутые кадры, видосики, регистрируйтесь в портал, просматривайте блог, делайте визиты на форум, начинайте еврейские знакомства.
ReplyDeleteВсем, кто ищет идеи для красивых причесок, рекомендую взглянуть на эти объемные косы для длинных волос. Отличный способ придать прическе стиль и элегантность! Объемные косы не только смотрятся шикарно, но и делают волосы визуально гуще. Подробности, советы и вдохновение можно найти по ссылке: https://twam.ru/volosy/obemnye-kosy-dlinnye-volosy/.
ReplyDeleteWhat Home Renovation Shows Are On Netflix house renovation application
ReplyDeleteХочу поделиться отличным секретом, как добавить немного остроумия к своему образу. Зацените вот эти фото - здесь крутейшие туфли! Уверен, такие роскошные модели точно станут ярким акцентом в вашем гардеробе. Мне помогли найти эту галерею с фото, и я даже не ожидал, что есть такое разнообразие стилей и цветов. А еще я нашел там красивейшее комбинированное пальто с мехом, которое смотрится просто невероятно. В общем, рекомендую всем пройти по ссылке https://fotofakt.ru/kombinirovannoe-palto-s-mehom и вдохновиться этими шикарными образами! Вам точно понравится. Расширьте свой гардероб и создайте неповторимый стиль с такой классной обувью!
ReplyDelete