pulumi/sdk/python/lib/test/test_test_mode.py
Joe Duffy 644d5dc916
Enable unit testing for Pulumi programs (#2638)
* Enable unit testing for Pulumi programs

This change enables rudimentary unit testing of your Pulumi programs, by introducing a `PULUMI_TEST_MODE` envvar that, when set, allows programs to run without a CLI. That includes

* Just being able to import your Pulumi modules, and test ordinary functions -- which otherwise would have often accidentally triggered the "Not Running in a CLI" error message
* Being able to verify a subset of resource properties and shapes, with the caveat that outputs are not included, due to the fact that this is a perpetual "dry run" without any engine operations occurring

In principle, this also means you can attach a debugger and step through your code.

* Finish the unit testing features

This change

1) Incorporates CR feedback, namely requiring that test mode be
   explicitly enabled for any of this to work.

2) Implements Python support for the same capabilities.

3) Includes tests for both JavaScript and Python SDKs.

* Add a note on unit testing to the CHANGELOG

* Use Node 8 friendly assert API

* Embellish the CHANGELOG entry a bit
2019-04-16 22:20:01 -07:00

77 lines
2.6 KiB
Python

# Copyright 2016-2018, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
import unittest
from pulumi import Output
from pulumi.errors import RunError
from pulumi.resource import CustomResource
from pulumi.runtime.settings import _set_project, _set_stack, _set_test_mode_enabled, get_project, get_stack
class FakeResource(CustomResource):
x: Output[float]
def __init__(__self__, name, x=None):
__props__ = dict()
__props__['x'] = x
super(FakeResource, __self__).__init__('python:test:FakeResource', name, __props__, None)
def async_test(coro):
def wrapper(*args, **kwargs):
loop = asyncio.new_event_loop()
loop.run_until_complete(coro(*args, **kwargs))
loop.close()
return wrapper
class TestModeTests(unittest.TestCase):
def test_reject_non_test_resource(self):
self.assertRaises(RunError, lambda: FakeResource("fake"))
def test_reject_non_test_project(self):
self.assertRaises(RunError, lambda: get_project())
def test_reject_non_test_stack(self):
self.assertRaises(RunError, lambda: get_stack())
@async_test
async def test_test_mode_values(self):
# Swap in temporary values.
_set_test_mode_enabled(True)
test_project = "TestProject"
_set_project(test_project)
test_stack = "TestStack"
_set_stack(test_stack)
try:
# Now access the settings -- in test mode, this will work.
p = get_project()
self.assertEqual(test_project, p)
s = get_stack()
self.assertEqual(test_stack, s)
# Allocate a resource and make sure its output property is set as expected.
x_fut = asyncio.Future()
res = FakeResource("fake", x=42)
res.x.apply(lambda x: x_fut.set_result(x))
x_val = await x_fut
self.assertEqual(42, x_val)
finally:
# Reset global state back to its previous settings.
_set_test_mode_enabled(False)
_set_project(None)
_set_stack(None)