Testing Embedded Systems With Cucumber
Cucumber isn't just for web apps. You can test just about anything with it—including embedded systems! In this talk, we'll look at several different facets of driving hardware from Cucumber, including:
Connecting to an Arduino using a custom serial protocol
Taking advantage of the TCP stack when it's available
Building the Cucumber wire protocol into your device
What to do when you can't modify the app under test
Driving more fully-featured devices such as the BeagleBone or RaspberryPi
By the end of the presentation, you'll have a handle on what the various options are for testing embedded devices, and which tradeoffs will apply to your system.
27. When /^I press the button$/ do
Arduino.press
Arduino.loop
end
Then /^the LEDs should read "(.*?)"$/ do
|expected|
expect(Arduino.leds).to eq(expected)
end
28. WHEN("^I press the button$") {
press();
loop();
}
THEN("^the LEDs should read "(.*?)"$") {
REGEX_PARAM(string, expected);
BOOST_CHECK_EQUAL(leds(), expected);
}
33. Then the LEDs should read "..O"
➡
["step_matches",
{"name_to_match":
"the LEDs should read"}]
➡
["success",
[{"id":"1",
"args":[{"val":"..O",
"pos":"22"}]}]]
35. extern "C" void respond_to_cucumber(
FILE* stream,
const char* message) {
string s = message;
Value v;
read(s, v);
Array& a = v.get_array();
string type = a[0].get_str();
// handle Cucumber message types
report_success(stream);
}
36. if (type == "step_matches") {
string name = step_name(v);
if (name == "I press the button") {
report_success(stream);
return;
} else if (...)
// ...
}
}
37. if (type == "step_matches") {
string name = step_name(v);
if (name == "I press the button") {
report_success(stream);
return;
} else if (...)
// ...
}
}
38. if (type == "step_matches") {
string name = step_name(v);
if (...) {
// ...
} else if (name.find("the LEDs") == 0)
const int START = 22;
string leds = name.substr(START, 3);
report_match(leds, START, stream);
return;
}
}
39. Then the LEDs should read "..O"
➡
["invoke",
{"id":"1",
"args":["..O"]}]
➡
["success", []]
40. if (type == "invoke") {
string id = step_id(v);
if (id == "0") {
press();
loop();
} else if (id == "1") {
// ...
}
}
}
41. if (type == "invoke") {
string id = step_id(v);
if (id == "0") {
press();
loop();
} else if (id == "1") {
// ...
}
}
}
42. if (type == "invoke") {
string id = step_id(v);
if (id == "0") {
// ...
} else if (id == "1") {
string expected = step_leds(v);
if (expected != leds()) {
report_failure("LEDs", stream);
return;
}
}
}
60. [When(@"I multiply (.*) and (.*)")]
public void WhenIMultiply(string a, string b)
{
window.Keyboard.Enter(a + "*" + b + "=");
}
[Then(@"I should get (.*)")]
public void ThenIShouldGet(string expected)
{
window.Get<Label>(expected);
}
71. When(/^I multiply (d+) and (d+)$/) do |a, b|
Calculator.multiply a, b
end
Then(/^I should get (d+)$/) do |n|
expect(Calculator.result).to eq(n.to_i)
end
72. require 'httparty'
class Calculator
include HTTParty
base_uri 'localhost:33333'
def self.multiply(a, b)
get "/multiply?multiplier=#{a}
&multiplicand=#{b}"
end
def self.result
get("/result").to_i
end
end