Skip to content
Panagiotis Georgiadis edited this page Jul 9, 2019 · 3 revisions

Welcome to the dudenetes wiki!

Write the features

The feature file is written in plain English, using some elements from gherkin. Go into the features directory and create a new test-case file that end with the *.feature suffix. For example: touch features/whatetever.feature

For example:

Feature: The name of the feature

    Here write something to describe your feature
    with as many lines as you like

    Scenario: Initialize skuba structure for cluster deployment
        Given you have LB with IP "10.10.10.10"
        When you run "skuba cluster init --control-plane 10.10.10.10 my-cluster"
        Then a folder my-cluster should be created
            """
            ls -l my-cluster | grep my-cluster
            """

Pass data to the code

It's easy to do that. Just put them in double-quotes "". For example:

Given you have LB with IP "10.10.10.10"

This ^ is passing the 10.10.10.10 IP address as an argument to a function. For example:

loadBalancerIP = arg1

Pass bash commands to the code

I suppose you are mostly familiar with running bash commands, for example using kubectl get nodes -A. This is something you can do either in the Go code, but it can also be taken fro the feature file. Sometimes the QA engineer wants to run this specific command explicitly, so this is a good use-case for that. First of all you can do that the same as before: just use double-quotes.

Another way to do that, you have to put the code inside three double-quotes: """ """.

For example:

Then a folder my-cluster should be created
            """
            ls -l my-cluster | grep my-cluster
            """

This snippet will pass the ls -l my-cluster | grep my-cluster as a DocString document into the code, which we can extract by using the .Content method. For example:

cmd := arg1.Content

Write the code

To test this feature, we need to write the code that parses this file, right? No. The testing code will be generated automatically. Just type:

godog features/whatever.feature

This will generate the testing code:

You can implement step definitions for undefined steps with these snippets:

func youHaveLBWithIP(arg1 string) error {
	return godog.ErrPending
}

func youRun(arg1 string) error {
	return godog.ErrPending
}

func aFolderMyclusterShouldBeCreated(arg1 *gherkin.DocString) error {
	return godog.ErrPending
}

func FeatureContext(s *godog.Suite) {
	s.Step(`^you have LB with IP "([^"]*)"$`, youHaveLBWithIP)
	s.Step(`^you run "([^"]*)"$`, youRun)
	s.Step(`^a folder my-cluster should be created$`, aFolderMyclusterShouldBeCreated)
}

So create a file called whatever_test.go and copy-paste that code inside (just don't forget to put the package main and the import for godog). So, your file will look like that:

package main

import (
	"github.com/DATA-DOG/godog"
)

package main

import (
	"github.com/DATA-DOG/godog"
	"github.com/DATA-DOG/godog/gherkin"
)

func youHaveLBWithIP(arg1 string) error {
	return godog.ErrPending
}

func youRun(arg1 string) error {
	return godog.ErrPending
}

func aFolderMyclusterShouldBeCreated(arg1 *gherkin.DocString) error {
	return godog.ErrPending
}

func FeatureContext(s *godog.Suite) {
	s.Step(`^you have LB with IP "([^"]*)"$`, youHaveLBWithIP)
	s.Step(`^you run "([^"]*)"$`, youRun)
	s.Step(`^a folder my-cluster should be created$`, aFolderMyclusterShouldBeCreated)
}

Edit the functions

We are going to modify the functions and do the actual testing, as currently they are returning a pending state as you can see (note: return godog.ErrPending). These 3 functions are called by the FeatureContext which will never touch.

The function for the loadbalancer

Here we are passing the loadbalancer IP. I don't know what I can do with that, I could maybe ping it or something like this, but for the moment I will store this variable into a global var, so I can use the IP of the LB for the other function in the future (just in case). So, not much of testing for this one; just grab the IP store it in a variable.

var LB string

func youHaveLBWithIP(arg1 string) error {
	LB = arg1
	return nil
}

Now run again the godog features/whatever.feature and pay attention that it says 1 passed, 1 pending, 1 skipped. Also there will be 3 different colors for that.

  • Green: passed
  • Yellow: pending (you have to write the code)
  • Blue: skipped
  • Red: failed

Run the skuba command

As a prereq I assume you have the binary (skuba) installed and your user should be able to run this. That is fine then to proceed.

Change this function, from this:

func youRun(arg1 string) error {
	return godog.ErrPending
}

into that:

func youRun(arg1 string) error {
	output, err := command.Run(arg1)
	if err != nil {
		return command.LogError(arg1, output)
	}
	return nil
}

And run again: godog features/whatever.feature. This will give you now 2 green tests and one pending:

Feature: The name of the feature
  Here write something to describe your feature
  with as many lines as you like

  Scenario: Initialize skuba structure for cluster deployment                # features/whatever.feature:6
    Given you have LB with IP "10.10.10.10"                                  # whatever_test.go:11 -> youHaveLBWithIP
    When you run "skuba cluster init --control-plane 10.10.10.10 my-cluster" # whatever_test.go:16 -> youRun
    Then a folder my-cluster should be created                               # whatever_test.go:25 -> aFolderMyclusterShouldBeCreated
      """
      ls -l my-cluster | grep my-cluster
      """
      TODO: write pending definition

1 scenarios (1 pending)
3 steps (2 passed, 1 pending)
25.167816ms

Run a bash command

Now we are going to use the DocString doc as an alternative to run the ls command pointed at the feature file. This is not very useful though in that case. It makes more sense to use it when you want to compare content of files so makes sure the configuration or the YAML definition is legit. Anyways, let's do it:

Change the function, from this:

func aFolderMyclusterShouldBeCreated(arg1 *gherkin.DocString) error {
	return godog.ErrPending
}

Into that:

func aFolderMyclusterShouldBeCreated(arg1 *gherkin.DocString) error {
	cmd := arg1.Content
	output, err := command.Run(cmd)
	if err != nil {
		return command.LogError(cmd, output)
	}
	return nil
}

Now run again the godog:godog features/whatever.feature. This time all tests should pass.

Feature: The name of the feature
  Here write something to describe your feature
  with as many lines as you like

  Scenario: Initialize skuba structure for cluster deployment                # features/whatever.feature:6
    Given you have LB with IP "10.10.10.10"                                  # whatever_test.go:11 -> youHaveLBWithIP
    When you run "skuba cluster init --control-plane 10.10.10.10 my-cluster" # whatever_test.go:16 -> youRun
    Then a folder my-cluster should be created                               # whatever_test.go:24 -> aFolderMyclusterShouldBeCreated
      """
      ls -l my-cluster | grep my-cluster
      """

1 scenarios (1 passed)
3 steps (3 passed)
26.463801ms

Final file

So the final test file looks like:

package main

import (
	"github.com/DATA-DOG/godog"
	"github.com/DATA-DOG/godog/gherkin"
	"github.com/drpaneas/dudenetes/pkg/command"
)

var LB string

func youHaveLBWithIP(arg1 string) error {
	LB = arg1
	return nil
}

func youRun(arg1 string) error {
	output, err := command.Run(arg1)
	if err != nil {
		return command.LogError(arg1, output)
	}
	return nil
}

func aFolderMyclusterShouldBeCreated(arg1 *gherkin.DocString) error {
	cmd := arg1.Content
	output, err := command.Run(cmd)
	if err != nil {
		return command.LogError(cmd, output)
	}
	return nil
}

func FeatureContext(s *godog.Suite) {
	s.Step(`^you have LB with IP "([^"]*)"$`, youHaveLBWithIP)
	s.Step(`^you run "([^"]*)"$`, youRun)
	s.Step(`^a folder my-cluster should be created$`, aFolderMyclusterShouldBeCreated)
}

Examples

Feature

Here's an example of a feature file

Pass

Here's an example of a successful run

Failure

Here's an example of a failed test Failed Test Case

No need for wrappers

If you want to run a kubectl command just run a kubectl command. So for example:

output, err := command.Run("kubectl get pods --all-namespaces")
if err != nil {
		return command.LogError(cmd, output)
	}
return nil