0

I try learn writing tests.


@Service
public class MailServiceImpl {
  @Autowired
  private SettingRepository settingRepository;

  public String buildTemplate(Template template, Map<String, String> templateParameters) {

    final String templateHeader = settingRepository.findByKey("test1").getValue();
    final String templateFooter = settingRepository.findByKey("test2").getValue();

    String content = replaceParameters(template.getContent(), templateParameters);

    StringBuilder builder = new StringBuilder();
    builder.append(templateHeader);
    builder.append(content);
    builder.append(templateFooter);

    return builder.toString();
  }

  private String replaceParameters(String content, Map<String, String> templateParameters) {
    for (Entry<String, String> parameter : templateParameters.entrySet()) {
      content = content.replace(
          String.format("{{ %s }}", parameter.getKey()),
          parameter.getValue()
      );
    }
    return content;
  }
}

public class MailServiceImplTest {

  @InjectMocks
  MailServiceImpl mailService;

  @Before
  public void setup() {
    initMocks(this);
  }

  @Test
  public void buildTemplate() {

    Template template = new Template();
    template.setContent("lorem ipsum");

    String content = mailService.buildTemplate(template, new HashMap<>());

    // assertion
  }
}

My problem is at this part:

final String templateHeader = settingRepository.findByKey("test1").getValue();

I get exception "NullPointerException", if is possible to tested this method without any code changes?

Database is MongoDB so do I need create "virtual" some documents with keys "test1", "test2"?


@Repository
public interface SettingRepository extends PagingAndSortingRepository<Setting, String> {

  Setting findByKey(String id);
}

Thanks for helping


Solution:

  @Test
  public void buildTemplate() {
Mockito.when(settingRepository.findByKey("test1")).thenReturn(new Setting());
    Mockito.when(settingRepository.findByKey("test2")).thenReturn(new Setting());
}
imokk
  • 1
  • 1
  • 1
    the code as you show us won't even compile, so I doubt this is your actual code. we can't diagnose your actual issues on code that doesn't reflect the actual code. settingRepository, for instance, doesn't exist in your code. You should get a compile time error when trying to use it – Stultuske May 16 '19 at 07:26

2 Answers2

1

You need to mock an instance of settingRepository. Simply using @InjectMocks won't magically inject MailServiceImpl's dependencies as Mockito has no clue what needs to be injected if you don't specify it.

public class MailServiceImplTest {

  @InjectMocks
  private MailServiceImpl mailService;

  @Mock
  private SettingRepository settingRepository;

I won't explain the difference between @InjectMocks and @Mock as it's out of scope for your specific question, but if you want to understand the difference between the two, you can check this question.

Thales Minussi
  • 6,965
  • 1
  • 30
  • 48
1

to me annotations is never really clear in terms of reading the code. so you can do it like this example:

...

import static org.mockito.Mockito.mock;

public class MailServiceImplTest {

  private SettingRepository settingRepository = mock(SettingRepository.class);

  private MailServiceImpl mailService = new MailServiceImpl(settingRepository)

}

this way, its more clear to the reader that you testing mail service and it has a new instance of it,

and you are mocking the repository.

and you can pass the repository in a constructor.

Amazia Gur
  • 1,692
  • 17
  • 16
  • I like your approach, @amazia. The best thing on it to me is that it shows how dependency injection via constructor is so great. These days people just don't see why it's beneficial to have constructor injection as Mockito completely abstracts it away from the developers. With your solution (if proramming to interfaces) we can even replace SettingRepository with an instance of our own mocks, which makes it very flexible if Mockito needs to be replaced or even removed. – Thales Minussi May 16 '19 at 08:37