对于正在进行的项目和改进我们的开发过程,我们考虑采用TDD作为开发理念。在研究最佳实践以及如何将新方法“推销”给我的同事/开发人员时,我遇到了BDD,并发现它更适合我们需要的东西以及某种方式下一次迭代TDD。问题是到目前为止我只尝试了tool Dan North开发的JBehave,我不能说我很惊讶。
设置在我看来很麻烦,我找不到非常合适的文档。另一方面,我也尝试了spock groovy工具,到现在为止我有点喜欢它。
问:有没有适合BDD使用的工具?
问:你会使用spock来处理引入另一种语言的开销吗?
答案 0 :(得分:37)
行为驱动开发只是一种无需任何工具即可使用的技术。你可以用BDD风格编写测试 - 例如使用should
启动测试方法,并使用此方法引入一些单独的功能。 When
和then
部分可以仅用注释替换,例如
@Test
public void should_do_something() {
// given
Something something = getSomething();
// when
something.doSomething();
// then
assertSomething();
// when
something.doSomethingElse();
// then
assertSomethingElse();
}
我对上述框架的看法:
JBehave的问题在于测试看起来像一个复杂的宇宙飞船。另一方面,它有适合您的规格的输出。
spock非常酷。紧凑的语法,漂亮的输出,许多功能,使用强大的groovy语言编写,这意味着可以与geb一起使用。 但它很时髦,对某人来说非常重要。
scalatest(用scala编写)和easyb(用groovy编写)都有与spock相同的缺点。 “......应该......”和“给定...然后”符号。规范在.story文件中,步骤实现在Java类中。这种方法作为定义规范的协作和通信工具非常有效,但对于低级编码通常会花费太多开销。
我还认为最成功的Java BDD框架是那些不是用Java编写的框架,因为Java语言没有Groovy或Scala创建的DSL(域特定语言)创建的灵活性。
答案 1 :(得分:15)
作为JGiven的作者,我不得不反对Java没有足够的灵活性来创建DSL。在JGiven中,BDD测试看起来如下:
@Test
public void users_can_login {
given()
.a_registered_user()
.and().the_login_page_is_shown();
when()
.the_user_enters_correct_credentials()
.and().the_login_button_is_pressed();
then()
.the_welcome_page_is_shown();
}
JGiven与JUnit或TestNg一起使用,您可以用普通Java编写测试。
答案 2 :(得分:13)
除非您的产品所有者/ qa /客户需要能够阅读测试,否则请使用Spock。它是一个非常简单的工具,但提高了测试的可读性。由于它强大的功能,你不需要Mockito,Hamcrest和AssertJ。它具有极好的参数化测试。事实上,它“只是”一个更好的JUnit - 一个自动执行简单任务的通用工具,无论是单元测试,集成测试还是验收测试。
害怕Groovy?为什么?它与java非常相似。学到的越多,代码就越有表现力,也越短。您的测试将更短,更易读。 Groovy是JVM更好的一面的门户药物。
不喜欢动态语言?好吧,它是测试,测试由CI服务器在每次提交之后运行,对吗?如果您的代码中断,您将在几分钟后知道它。没有CI服务器或没有定期运行测试?然后不要费心选择测试框架并修复您的过程。破碎的测试是没用的,如果你不经常进行测试,它们很快就会破裂。
如果需要,请使用JBehave / Cucumber;否则,请使用Spock。
答案 3 :(得分:3)
另一种选择是频谱 - 请参阅https://github.com/greghaskins/spectrum
Spectrum支持RSpec / Mocha语法,并且在其下一个版本中还将支持Gherkin语法以及JUnit规则集成(因此它通过@Rule
和@ClassRule
成员与Mockito,Spring等互操作)
完全披露 - 我是此OS项目的撰稿人
示例:
@RunWith(Spectrum.class)
public class TestSomething {{
Supplier<Something> freshTestObject = let(Something::new);
describe("The component", () -> {
it("is tested by specs", () -> {
// the "let` above gives us a new instance of the object
// in each spec
freshTestObject.get().doSomething();
// using your favourite assertion framework
assertThat(something.get().getSomething()).isEqualTo(42);
});
});
}}
Spectrum在JUnit控制台中输出分层测试结果。它的优势在于将spec执行的Java实现与spec定义混合在一起 - 这可能比依赖于特征文件和粘合代码来解析它们的框架更直接,特别是如果需要通过结果从测试的一个步骤到另一个步骤。
Spectrum旨在成为多语言,因此对于几个现有框架的用户来说似乎应该很熟悉。
答案 4 :(得分:1)
此外,我是COLA Tests的作者,一个支持完整小黄瓜语法的新框架(与Cucumber完全相同),它的设置非常简单,特别是与JBehave并不需要JUnit runner。
基本上只使用你已经习惯的任何文库!
以下是Spring Controller Test的示例(可以从文件加载故事):
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = { WebAppContext.class })
public class HelloWorldControllerTest extends BaseColaTest {
private final String stories =
"Feature: Introduce REST endpoint\n"
+ "Scenario: Should say hello\n"
+ "Given a web endpoint\n"
+ "When hit by a get request\n"
+ "Then the HTTP status will be OK\n"
+ "And the body will say hello world";
@Resource
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private ResultActions result;
@Given("a web endpoint")
public void given() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@When("hit by a get request")
public void when() throws Exception {
result = mockMvc.perform(get("/helloWorld"));
}
@Then("the HTTP status will be OK")
public void thenOk() throws Exception {
result.andExpect(status().isOk());
}
@Then("the body will say hello world")
public void thenHello() throws Exception {
result.andExpect(content().string("Hello World!"));
}
}
答案 5 :(得分:1)
给Ginkgo4j一个去。它使用Java 8的lamda来反映Ruby的RSpec和Go的Ginkgo使用的方法。
该库允许您构建富有表现力,内容丰富的测试。
```
package com.github.paulcwarren.ginkgo4j.examples;
import static com.github.paulcwarren.ginkgo4j.Ginkgo4jDSL.*;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.runner.RunWith;
import com.github.paulcwarren.ginkgo4j.Ginkgo4jRunner;
@RunWith(Ginkgo4jRunner.class)
public class BookTests {
private Book longBook;
private Book shortBook;
{
Describe("Book", () -> {
BeforeEach(() -> {
longBook = new Book("Les Miserables", "Victor Hugo", 1488);
shortBook = new Book("Fox In Socks", "Dr. Seuss", 24);
});
Context("Categorizing book length", () -> {
Context("With more than 300 pages", () -> {
It("should be a novel", () -> {
assertThat(longBook.categoryByLength(), is("NOVEL"));
});
});
Context("With fewer than 300 pages", () -> {
It("should be a short story", () -> {
assertThat(shortBook.categoryByLength(), is("NOVELLA"));
});
});
});
});
}
}
```
也支持Spring。
(完全披露。我是这个图书馆的作者)。