4

We are desperately trying to adopt BDD in our company at the moment and (as usual?) there are lots of questions about the right way to do BDD now.

One of my favorites is the following.

So let's assume we have a service like that (original one is a little more complex, but simplifying it for the example):

@Service
public class RegistrationService {

    private final UserRegistrationRepository userRegistrationRepository;

    @Autowired
    public RegistrationService(
            final UserRegistrationRepository userRegistrationRepository) {
        this.userRegistrationRepository = userRegistrationRepository;
    }

    @Override
    public void register(final String email) {
        final UserRegistration userRegistration = createUserRegistration(email);
        userRegistrationRepository.deleteByEmail(email);
        userRegistrationRepository.save(userRegistration);
    }

    ...
}

Currently our RegistrationServiceUnitTest tests it as follows:

  • Method deleteByEmail is called
  • Method save is called after it
  • Parameter of save is as expected

Some people may say that's is strict correspondence with Clean Code (which I don't agree), but I don't like the approach mostly because it tests that the method is implemented in the exact way, rather than its behavior... And the behavior is that there should be not more than 1 record in DB after the method.

So what is the right BDD-way to tests methods like that? Is the one above ok with BDD technique and I should just calm down and live with that? :)

UPDATE: As I see a BDD test here:

  1. Given an empty database, When I call register(..), Then database contains 1 correct record
  2. Given a database with record for <email>, When I call register(<email>), Then database contains 1 correct record

But yes, that actually moves it to the integration test level (or even higher). And even worse it makes the test dependent on DB structure/persistence layer...

  • Maybe asking on programmers.stackexchange.com will get you more and better answers. As this is more a programming question than a testing question. Although TDD and BDD sounds like testing its more about designing and maintaining code. Most tests actually do not test anything like you already concluded. – Niels van Reijmersdal Oct 6 '16 at 9:56
  • Could you show your BDD implementation? – Paul Muir Oct 6 '16 at 12:05
  • @PaulMuir, if that's a question for me, I see it like that: 1. Given an empty database, When I call register(..), Then database contains 1 correct record 2. Given a database with record for <email>, When I call register(<email>), Then database contains 1 correct record But yes, that actually moves it to the integration test level (or even higher). And even worse it makes the test dependent on DB structure/persistence layer... – FlasH from Ru Oct 6 '16 at 14:32
  • @FlasHfromRu It is. You listed the code you are wanting to test but I do not see any of the implementation of your BDD. Just a small sample will suffice. – Paul Muir Oct 6 '16 at 14:33
  • @PaulMuir, added my vision to the question (always forget about the comments window posting its content on the first enter...) – FlasH from Ru Oct 6 '16 at 14:44
1

To me personally the power in BDD lies in the reusability of the code. I use Specflow/C# currently and have used Cucumber4Java and Spock in the past.

By having your test methods broken down into Actions, named and then allowed to be recycled in multiple tests it creates a stronger framework for tests. It essentially gives your tests more of an OOP design.

So, using your example:

[Given(@"I call deleteByEmail with email (.*?)"]
[When(@"I call deleteByEmail with email (.*?)"]
public void ICallDeleteByEmail(string email) { 
    object.deleteByEmail(email); 
}
[Given(@"I call save")]
[When(@"I call save")]
public void ICallSave() { 
    object.save(); 
}

[Then(@"The record with email (.*?) will be in the database")]
public void checkDatabaseForEmail(string email) { 
    Assert.IsPresent(database.lookForRecord(email)); 
}

[Then(@"The record with email (.*?) will not be in the database")]
public void checkDatabaseForEmail(string email) { 
    Assert.IsNotPresent(database.lookForRecord(email)); 
}

Now your tests can use these methods in several ways:

Given I call save
Then the record with email a@b.com will be in the database

Given I call save
When I call deleteByEmail with email a@b.com
Then the record with email a@b.com will not be in the database

The list goes on and on, you can use parameterized tables and easily maintain the code. If the deleteByEmail function needs to be updated, you update it at one place and it updates for all of your tests.

0

I think there are multiple ways of looking at writing unit-tests:

  • Tests that drive design (in BDD, TDD cycles)
  • Tests that safe-guard functional behavior

The test you describe are used to drive design, but will be extra work to maintain when you change the design, since you will need to update all the tests. Becareful how many of those you keep after the design is done. Adding integration like tests to safe-guard the actual behavior of a class might be more valuable in the long run. Probably the behavior of the class will not change that much.

I think BDD is a technique to capture requirements and automate them. They should be used to have a common communication between developers and stakeholders/users. Writing unit-tests using BDD doesn't make a lot of sense to me.

I think the BDD used to write unit-tests is not really BDD. Its just a bit more descriptive way of writing and designing your test-cases. Unless you really practice the cycle of writing a failing BDD test first. But what you described sounds like step-by-step way how the code needs to work instead of a behavior. Also I guess it is written after the class code was created and not with a test-first pattern.

the gotcha with TDD is that too many developers focused on the "How" when writing their unit tests, so they ended up with very brittle tests that did nothing more than confirm that the system does what it does.

read more here...

To answer your question:

So what is the right BDD-way to tests methods like that?

BDD-way tests are more fit for integration level tests. Although I like to write my unit-test names using the Given/When/Then pattern to make me think about what am I trying to test here.

Also keep in mind things like BDD are tools, not a methodology you can always follow and use in all situations. Stop, think and act. :)

Reads:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.