Writing tests is a key task in our day-to-day work to ensure the quality of our code. While we usually run unit tests during the build process, others types of tests, such as acceptance tests, require to have the application previously deployed in the environment.
In a previous post, we reviewed Helm as a tool to deploy our applications into a Kubernetes cluster. Today we will explore a feature that could make our life easier when faced with this last type of testing.
We will use the default ASP.NET Core Web API template to have an application to test. In our case, the project will be called helm-sandbox-app
. Add a Dockerfile
to the project with the following content:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["helm-sandbox-app/helm-sandbox-app.csproj", "helm-sandbox-app/"]
RUN dotnet restore "helm-sandbox-app/helm-sandbox-app.csproj"
COPY . .
WORKDIR "/src/helm-sandbox-app"
RUN dotnet build "helm-sandbox-app.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "helm-sandbox-app.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "helm-sandbox-app.dll"]
Next, we will create an MSTest Test project called helm-sandbox-tests
. Add the following NuGet package to the project:
Modify the default test class with the following code:
[TestClass]
public class WeatherForecastTests
{
private readonly string _uri;
[TestMethod]
public async Task should_return_ok()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_uri);
var response = await client.GetAsync("WeatherForecast");
Assert.IsNotNull(response);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
}
public WeatherForecastTests()
{
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
_uri = config["Uri"];
}
}
Add a Dockerfile
to the project with the following content:
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["helm-sandbox-tests/helm-sandbox-tests.csproj", "helm-sandbox-tests/"]
RUN dotnet restore "helm-sandbox-tests/helm-sandbox-tests.csproj"
COPY . .
WORKDIR "/src/helm-sandbox-tests"
ENTRYPOINT ["dotnet", "test", "helm-sandbox-tests.csproj","--no-restore"]
Time to generate our local Docker images (one for your API and the other to test the API). At the solution level, run (don't forget the dot at the end):
docker build -t raulnq/helm-sandbox-app:1.0 -f .\helm-sandbox-app\Dockerfile .
docker build -t raulnq/helm-sandbox-tests:1.0 -f .\helm-sandbox-tests\Dockerfile .
Let's create our helm package:
mkdir helm
cd helm
helm create helm-sandbox-app
Keep only these files in your Helm package (rename the default test-connection.yaml to test.yaml):
helm\helm-sandbox-app
|--test
| `-- test.yaml
|-- templates
| |-- _helpers.tpl
| |-- deployment.yaml
| `-- service.yaml
|-- .helmignore
|-- Chart.yaml
`-- values.yaml
Modify the deployment.yaml
file as follows:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "helm-sandbox-app.fullname" . }}
labels:
{{- include "helm-sandbox-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "helm-sandbox-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "helm-sandbox-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
The service.yaml
file:
apiVersion: v1
kind: Service
metadata:
name: {{ include "helm-sandbox-app.fullname" . }}
labels:
{{- include "helm-sandbox-app.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "helm-sandbox-app.selectorLabels" . | nindent 4 }}
The test.yaml
file:
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "helm-sandbox-app.fullname" . }}-test"
labels:
{{- include "helm-sandbox-app.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test-success
"helm.sh/hook-delete-policy": before-hook-creation
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.tests.repository }}:{{ .Values.tests.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.tests.pullPolicy }}
env:
- name: Uri
value: "http://{{ include "helm-sandbox-app.fullname" . }}:{{ .Values.service.port }}"
restartPolicy: Never
And the values.yaml
file:
replicaCount: 1
image:
repository: raulnq/helm-sandbox-app
pullPolicy: IfNotPresent
tag: "1.0"
tests:
repository: raulnq/helm-sandbox-tests
pullPolicy: IfNotPresent
tag: "1.0"
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
Run the following command to install your chart in your local Kubernetes cluster:
helm upgrade release-helm-sandbox-app helm-sandbox-app --install --debug
And finally, the helm test
command:
helm test release-helm-sandbox-app
If everything goes fine, we will see an output like this:
Pod release-helm-sandbox-app-test pending
Pod release-helm-sandbox-app-test pending
Pod release-helm-sandbox-app-test pending
Pod release-helm-sandbox-app-test running
Pod release-helm-sandbox-app-test succeeded
NAME: release-helm-sandbox-app
LAST DEPLOYED: Sat Jun 18 10:57:44 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: release-helm-sandbox-app-test
Last Started: Sat Jun 18 22:59:42 2022
Last Completed: Sat Jun 18 22:59:47 2022
Phase: Succeeded
We can check the logs of our pod with:
kubectl logs release-helm-sandbox-app-test
Having as a result:
helm-sandbox-tests -> /src/helm-sandbox-tests/bin/Debug/net6.0/helm-sandbox-tests.dll
Test run for /src/helm-sandbox-tests/bin/Debug/net6.0/helm-sandbox-tests.dll (.NETCoreApp,Version=v6.0)
Microsoft (R) Test Execution Command Line Tool Version 17.2.0 (x64)
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: 115 ms - /src/helm-sandbox-tests/bin/Debug/net6.0/helm-sandbox-tests.dll (net6.0)
Here you can find the official documentation about the helm test
command. The code of this post can be found here.