pulumi
pulumi copied to clipboard
Mocking the pulumi automation API
I'm using the Pulumi automation API in an application that i'm writing however I am having trouble writing tests that mock the functionality. Do mocks already exist for the pulumi automation API and if so where can I find examples?
Functionality I want to mock:
const stack = await LocalWorkspace.createOrSelectStack({
stackName: this.name,
workDir: this.destinationRoot(),
}, {
secretsProvider: `awskms://${keyId}?region=${this.region}`,
projectSettings: {
name: this.name,
runtime: 'nodejs',
backend: {
url: `s3://${bucket}`,
},
},
});
We do not have mocks available for Automation API, we don't mock for our own testing and have a CLI installed and authenticated to run with our tests. But this is a nice suggestion!
Unsure what your intentions are with needing to mock out automation api, but here are a few possible suggestions you could try:
- Using a local backend for testing if the concern is polluting your pulumi org or forcing traffic over the wire
- Mocking using
spyOn
from jest - Implementing a "test" impl of the Workspace interface (this might be a lot of work, and we have not tried this)
I just came here because I have the same requirement. I think the last point, to implement each function of the Workspace
interface wouldn't be too hard using the gomock generator (at least for go).
However this comes with another issue: The workspace is always created within utility functions, such as here, in the auto.UpsertStackInlineSource
function: https://github.com/pulumi/pulumi/blob/f4b76a901a5ab9e89723c9ed9c78c4f86bde0024/sdk/go/auto/local_workspace.go#L1310
Even recreating the few lines of this function in my own code doesn't easily work, because it uses private sub functions such as this one: https://github.com/pulumi/pulumi/blob/f4b76a901a5ab9e89723c9ed9c78c4f86bde0024/sdk/go/auto/local_workspace.go#L1302
So all this would need to be recreated as well.
It would be awesome if we could just hand over a Workspace
pointer to functions like UpsertStackInlineSource
. That way we could send either a mock or the real one.
Upon thinking about this a little further, I actually found a solution that works for me. So posting here in case anyone else stumbles upon this:
At the place where I'm calling auto.NewLocalWorkspace
I changed it to this (truncated to essentials):
import (
// other imports
"github.com/pulumi/pulumi/sdk/v3/go/auto"
_ "go.uber.org/mock/mockgen/model"
)
//go:generate mockgen -destination=./mocks/workspace.go -package mocks github.com/pulumi/pulumi/sdk/v3/go/auto Workspace
var (
autoUpsertStackInlineSource = auto.UpsertStackInlineSource
)
func Get(ctx context.Context) (auto.Stack, error) {
var stack auto.Stack
// create stack
stack, err := autoUpsertStackInlineSource(
ctx,
auto.FullyQualifiedStackName(config.Org, config.ProjectName, stackName),
config.ProjectName,
modules.Run,
)
// rest of code
return stack, err
}
Then in my _test.go
file I'm doing this (truncated to essentials):
func TestSomething(t *testing.T) {
tests := map[string]struct{}{
"happy case": {},
// other test cases
}
for name := range tests {
t.Run(name, func(t *testing.T) {
ctrl := gomock.NewController(t)
w := mocks.NewMockWorkspace(ctrl)
// mock whatever workspace function your code is calling
w.EXPECT().CreateStack(gomock.Any(), gomock.Any()).Return(nil)
w.EXPECT().Program().Return(func(ctx *pulumi.Context) error { return nil })
autoUpsertStackInlineSource = func(ctx context.Context) (auto.Stack, error) {
return auto.NewStack(ctx, "test-stack", w)
}
Run(nil, nil)
})
}
}
The workspace mock can just be generated via go generate ./...
So I'm basically doing the dependency injection on one level higher, via autoUpsertStackInlineSource = auto.UpsertStackInlineSource
and overwriting that function in the test using a mock workspace to init the stack.
With that I'd actually say there's no todo on this issue.