Goal : testing TDD in a typical enterprise Java environment.
Context :
Frameworks used (even if it's overkill, I practice project based learning) :
DAO : Hibernate
Spring IoC
Front : Spring MVC + Twitter Boostrap (if possible)
TDD : JUnit
DB : PostgreSQL
My project is a simple billing system which would help freelancers create, edit and print/send bills to customers.
Once my project is created and configured, I don't know where to start.
Let's say my first my first feature is to create a bill with a unique number and a title.
Question : What should I test first ?
the Domain layer with a createBill(String title) method which would generate a unique Serial Number ? I'd mock the DB layer.
the UI first, mocking the service layer ? I don't know how to do it.
Thanks in advance for your answers,
Cheers
Start with a test :-)
What does your system do?
public class BillingSystemTest {
#Test
public void generatesBills() {
Bill bill = new BillingSystem().generate()
assertNotNull(bill)
}
}
First test complete!
Make it pass, move on to the next test...
#Test
public void generatesAnInvoiceNumberForEachBill() {
Bill bill = new BillingSystem().generate()
assertEquals(1, bill.getNumber())
}
// ...and the next
#Test
public void generatesUniqueInvoiceNumbersForEachBill() {
BillingSystem bs = new BillingSystem()
assertEquals(1, bs.generate().getNumber())
assertEquals(2, bs.generate().getNumber())
}
#Test
public void generatesAnInvoiceSubjectWhenNoneIsSpecified() {
Bill bill = new BillingSystem().generate()
assertEquals("Invoice #1 from ACME Corp.", bill.getSubject())
}
#Test
public void allowsForCustomSubjectsOnBills() {
Bill bill = new BillingSystem().generate("Custom subject")
assertEquals("Custom subject", bill.getSubject())
}
I've obviously skipped the refactoring steps here, but now that you have the tests, and the code that goes with it, you need to evaluate it for more opportunity. I'm imagining the code looking something like this.
public class BillingSystem {
private nextInvoiceNumber = 1;
public Bill generate() {
return generate("Invoice #" + nextInvoiceNumber + " from ACME Corp.");
}
public Bill generate(String subject) {
Bill bill = new Bill(nextInvoiceNumber, subject)
nextInvoiceNumber++
return bill;
}
}
Looking at this code, it seems ok, but may violate the Single Responsibility Principle (SRP). Here the BillingSystem generating a bill as well as managing the invoice number. This is an opportunity for refactoring. After the refactoring, your design may look something like this:
public class BillingSystem {
private InvoiceNumbering invoiceNumbering = new InvoiceNumbering()
public Bill generate() {
return generate("Invoice #" + invoiceNumbering.peekNext() + " from ACME Corp.");
}
public Bill generate(String subject) {
Bill bill = new Bill(invoiceNumbering.generateNext(), subject)
nextInvoiceNumber++
return bill;
}
}
Your design is better and your tests all pass. Next thing to do is refactor out the tests to remove the implementation details from them. They may end up looking something like:
#Test
public void generatesBills() {
Bill bill = new BillingSystem().generate()
assertNotNull(bill)
}
#Test
public void generatesAnInvoiceNumberForEachBill() {
// Using hand rolled mocks
MockInvoiceNumbering in = new MockInvoiceNumbering()
in.generateNextShouldReturn(4)
Bill bill = new BillingSystem(in).generate()
assertEquals(4, bill.getNumber())
}
#Test
public void generatesUniqueInvoiceNumbersForEachBill() {
MockInvoiceNumbering in = new MockInvoiceNumbering()
BillingSystem bs = new BillingSystem(in)
bs.generate();
bs.generate();
assertEquals(2, in.numberOfTimesGenerateNextWasCalled)
}
#Test
public void generatesAnInvoiceSubjectWhenNoneIsSpecified() {
Bill bill = new BillingSystem().generate()
assertEquals("Invoice #1 from ACME Corp.", bill.getSubject())
}
#Test
public void allowsForCustomSubjectsOnBills() {
Bill bill = new BillingSystem().generate("Custom subject")
assertEquals("Custom subject", bill.getSubject())
}
As a part of this refactoring, you would likely create some tests around your InvoiceNumbering class.
Hope that's enough of a start. Left a lot out. :-)
Hope that helps!
Brandon
Related
I have a custom query in the repository.
And the service that calls this method from the repository.
I want to write a unit test.
What should I test for a repository or a service?
Or should I do 2 absolutely identical tests?
You should unit test everything. Test happy-paths + sad-paths. The more you test the better your results will be.
For repo tests it should be enough to test 1 happy and 1 sad (either you receive the data or you don't)
Service tests require more time, say you have a few if-conditions then you would want to test those specific cases as well.
As an example:
#DataJpaTest
class RoleRepoTest {
#Autowired
private RoleRepo roleRepo;
#PersistenceContext
private EntityManager entityManager;
#Test
void itShouldfindRoleByName() {
// given
Role role = new Role(null, "administrator");
roleRepo.save(role);
flushAndClear();
// when
Role roleExpected = roleRepo.findByName("administrator");
// then
assertThat(roleExpected.getName()).isEqualTo(role.getName());
}
#Test
void itShouldNotfindRoleByName() {
// when
Role expectedRole = roleRepo.findByName("name");
assertThat(expectedRole).isNull();
}
private void flushAndClear() {
entityManager.flush();
entityManager.clear();
}
}
Make sure to always flush & clear. This will avoid your tests from failing when running all at once. It ensures that each test is run in a clean and isolated environment. Without flushing and clearing the database, tests may be affected by data that was created or modified during previous tests.
For Service tests I've got the following example:
#Test
void deleteUser_givenUser_whenTryingToDeleteSameUser_thenUserDeleted() {
// given
given(securityContext.getAuthentication().getPrincipal()).willReturn(userDetails);
given(userDetails.getUsername()).willReturn(user.getUsername());
given(userRepo.findByUsername(user.getUsername())).willReturn(user);
userIsStudent();
String username = user.getUsername();
// when
authService.deleteUser(new DeleteAccountDto(username));
// then
verify(userRepo).deactivateUser(username);
}
#Test
void deleteUser_givenLoggedInUserNotAdmin_whenTryingToDeleteUserWithOtherUsername_ThrowError() {
// given
String username = studentUser.getUsername();
// when + then
assertThatThrownBy(() -> authService.deleteUser(new DeleteAccountDto(username)))
.isInstanceOf(AccountException.class)
.hasMessage("De gebruikersnaam is niet gevonden.");
verify(userRepo, never()).deactivateUser(username);
}
#Test
void deleteUser_givenLoggedInUserIsAdmin_whenTryingToDeleteExistingUser_ThenUserIsDeleted() {
given(securityContext.getAuthentication().getPrincipal()).willReturn(userDetails);
given(userDetails.getUsername()).willReturn(user.getUsername());
userIsAdmin();
// given
given(userRepo.findByUsername(studentUser.getUsername())).willReturn(studentUser);
String username = studentUser.getUsername();
// when
authService.deleteUser(new DeleteAccountDto(username));
// then
verify(userRepo).deactivateUser(username);
}
#Test
void deleteUser_givenLoggedInUserIsAdmin_WhenTryingToDeleteLoggedInUser_ThenThrowError() {
given(securityContext.getAuthentication().getPrincipal()).willReturn(userDetails);
given(userDetails.getUsername()).willReturn(user.getUsername());
userIsAdmin();
// when + then
given(userRepo.findByUsername(adminUser.getUsername())).willReturn(adminUser);
assertThatThrownBy(() -> authService.deleteUser(new DeleteAccountDto(adminUser.getUsername())))
.isInstanceOf(AccountException.class)
.hasMessage("Administrators can't be deleted");
}
#Test
void deleteUser_GivenLoggedInUserIsAdmin_WhenTryingToDeleteOtherAdmin_ThenThrowError() {
userIsAdmin();
given(securityContext.getAuthentication().getPrincipal()).willReturn(userDetails);
given(userDetails.getUsername()).willReturn(user.getUsername());
given(userRepo.findByUsername(otherAdminUser.getUsername())).willReturn(otherAdminUser);
// when + then
assertThatThrownBy(() -> authService.deleteUser(new DeleteAccountDto(otherAdminUser.getUsername())))
.isInstanceOf(AccountException.class)
.hasMessage("Admins can't be deleted");
}
Same thing applies for custom queries. Just try to test all your logic in your service-class
I hope this answers your question.
I'm unit testing a Spring boot web app with Mockito. One of my methods is returning a Actorrest, but if I try to test it, I get compilation errors.
This is the test I wrote:
#Test
void AddActorToChapters() throws NetflixException{
when(actorRepository.findById(MockData.getActor().getId())).thenReturn(Optional.of(MockData.getActor()));
when(chapterService.findChapterById(MockData.getChapter().getId())).thenReturn((MockData.getChapter()));
service.AddActorToChapter(MockData.getChapter().getId(), MockData.getActor().getId());
assertEquals(MockData.getActor().getChapters(), MockData.getChapter());
}
And this is the method I'm trying to test:
#Override
public ActorParticipant AddActorToChapter(Long idActor, Long idChapter) throws NetflixException {
Actor actor = actorRepository.findById(idActor)
.orElseThrow(() -> new NotFoundException("Actor id not found - " + idActor));
if (actorRepository.existsByIdAndChapters_Id(idActor, idChapter)) {
throw new DuplicateException("Actor id found - " + idActor);
}
Chapter chapter = chapterService.findChapterById(idChapter);
actor.getChapters().add(chapter);
actorRepository.save(actor);
ActorParticpateMapper mapper = new ActorParticpateMapper();
return mapper.mapActorToActorParticipant(actor);
}
and the database mockdata are
public class MockData {
public static Actor getActor() {
Actor actor = new Actor();
actor.setId(1L);
actor.setName("ali");
actor.setNationality("ameriacn");
actor.getChapters().add(getChapter());
return actor;
}
public static Chapter getChapter() {
Chapter chapter = new Chapter();
chapter.setId(1L);
chapter.setName("Chapter 7");
return chapter;
}}
log error
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=AddActorToChapters], {ExactMatcher:fDisplayName=AddActorToChapters(actortest.ActorServiceTesting)], {LeadingIdentifierMatcher:fClassName=actortest.ActorServiceTesting,fLeadingIdentifier=AddActorToChapters]] from org.junit.internal.requests.ClassRequest#64b8f8f4
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:83)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:74)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:49)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:513)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
With the JUnit 4 test runner you are using, the test methods must be defined as public.
I am trying to write a few SONARQUBE custom rules for my project.
After reading up the below document -
https://docs.sonarqube.org/display/PLUG/Writing+Custom+Java+Rules+101
and
https://github.com/SonarSource/sonar-custom-rules-examples,
I created a custom rule like these classes below -
The Rule file:
#Rule(key = "MyAssertionRule")
public class FirstSonarCustomRule extends BaseTreeVisitor implements JavaFileScanner {
private static final String DEFAULT_VALUE = "Inject";
private JavaFileScannerContext context;
/**
* Name of the annotation to avoid. Value can be set by users in Quality
* profiles. The key
*/
#RuleProperty(defaultValue = DEFAULT_VALUE, description = "Name of the annotation to avoid, without the prefix #, for instance 'Override'")
protected String name;
#Override
public void scanFile(JavaFileScannerContext context) {
this.context = context;
System.out.println(PrinterVisitor.print(context.getTree()));
scan(context.getTree());
}
#Override
public void visitMethod(MethodTree tree) {
List<StatementTree> statements = tree.block().body();
for (StatementTree statement : statements) {
System.out.println("KIND IS " + statement.kind());
if (statement.is(Kind.EXPRESSION_STATEMENT)) {
if (statement.firstToken().text().equals("Assert")) {
System.out.println("ERROR");
}
}
}
}
}
The Test class:
public class FirstSonarCustomRuleTest {
#Test
public void verify() {
FirstSonarCustomRule f = new FirstSonarCustomRule();
f.name = "ASSERTION";
JavaCheckVerifier.verify("src/test/files/FirstSonarCustom.java", f);
}
}
And finally - the Test file:
class FirstSonarCustom {
int aField;
public void methodToUseTestNgAssertions() {
Assert.assertTrue(true);
}
}
The above Test file would later be my Project's source code.
As per the SONAR documentation - the // Noncompliant is a mandatory comment in my Test file. Thus my first question is should I add this comment everywhere in my Source code too?
If yes - is there any way I can avoid adding this comment, because I do not want to add that code refactoring exercise all over.
Can someone suggest me what I need to do here?
I am using SONARQUBE 6.3.
This comment is only used by the test framework (JavaCheckVerifier class) to test the implementation of your rule. It is not mandatory in any way and for sure you don't need it in your real code.
In jFugue 4.0 there's a nice function:
Transforming Patterns with PatternTransformer
but all pattern transformers are removed in jFugue 5.0. I understand it must be replaced with something cool. but what to do in jFugue 5.0 please? i get no clue. I googled but have so far had no outcome.
The class "PatternTransformer" has gone, but transforming patterns has never been easier!
In older versions of JFugue, there was actually very little difference between a PatternTransformer and a ParserListener. Older versions of JFugue also referred to a PatternTool, which was like a Transformer but instead of transforming a pattern, it would just measure it; for example, you could write a tool to tell you what instruments were used in a piece.
To transform a Pattern in JFugue, just create a class that implements ParserListener (or extends ParserListenerAdapter), and add it as a listener to a parser - such as a StaccatoParser:
For example, here's a tool that finds what instruments are used in a piece:
public class InstrumentTool extends ParserListenerAdapter
{
private List<String> instrumentNames;
public InstrumentTool() {
super();
instrumentNames = new ArrayList<String>();
}
#Override
public void onInstrumentParsed(byte instrument) {
String instrumentName = MidiDictionary.INSTRUMENT_BYTE_TO_STRING.get(instrument);
if (!instrumentNames.contains(instrumentName)) {
instrumentNames.add(instrumentName);
}
}
public List<String> getInstrumentNames() {
return this.instrumentNames;
}
}
and here's how to use it:
MidiParser midiParser = new MidiParser();
InstrumentTool instrumentTool = new InstrumentTool();
midiParser.addParserListener(instrumentTool);
midiParser.parse(MidiSystem.getSequence(new File("filename")));
List<String> instrumentNames = instrumentTool.getInstrumentNames();
for (String name : instrumentNames) {
System.out.println(name);
}
There's a new class in JFugue 5 that lets you chain ParserListeners together. This would let you create a chain of listeners that each modify a pattern before sending events to the next listener in the chain. For example, suppose you have a pattern, and you want to transform all of the instruments (say, change GUITAR to PIANO); then you want to take any note played with PIANO and stretch its duration by two; then you want to take any note with a new duration greater than 2.0 (two whole notes) and you want to change its octave. A bit of a crazy example, but it shows the need for a "chaining" series of parser listeners.
Here's a demo example that uses chaining. This class reads a MIDI pattern; it then changes all of the instruments, and then it creates a Staccato pattern from the original MIDI.
public class ChainingParserListenerDemo {
public static void main(String[] args) throws InvalidMidiDataException, IOException {
MidiParser parser = new MidiParser();
InstrumentChangingParserListener instrumentChanger = new InstrumentChangingParserListener();
StaccatoParserListener staccatoListener = new StaccatoParserListener();
instrumentChanger.addParserListener(staccatoListener);
parser.addParserListener(instrumentChanger);
parser.parse(MidiSystem.getSequence(new File("filename")));
System.out.println("Changed "+instrumentChanger.counter+" Piano's to Guitar! "+ staccatoListener.getPattern().toString());
}
}
class InstrumentChangingParserListener extends ChainingParserListenerAdapter {
int counter = 0;
#Override
public void onInstrumentParsed(byte instrument) {
if (instrument == MidiDictionary.INSTRUMENT_STRING_TO_BYTE.get("PIANO")) {
instrument = MidiDictionary.INSTRUMENT_STRING_TO_BYTE.get("GUITAR");
counter++;
}
super.onInstrumentParsed(instrument);
}
}
I currently have a simple website setup with ASP.NET Core MVC (.NET 4.6.1), and I would like to periodically do some processes like automatically send emails at the end of every day to the registered members.
After doing some searching, I came across two common solutions - Quartz.NET and FluentScheduler.
Based on this SO thread, I found the approach of using FluentScheduler more easier to digest and use for my simple task. After quickly implementing the following lines of code into my Program.cs class, I had the emails going out successfully every minute (for testing purposes).
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
var registry = new Registry();
JobManager.Initialize(registry);
JobManager.AddJob(() => MyEmailService.SendEmail(), s => s
.ToRunEvery(1)
.Minutes());
host.Run();
}
}
However, now apart from sending emails I also need to do some back-end processing for e.g. updating the user records in the DB when mails are being sent out. For this, I normally inject my Entity Framework Context into the constructor of my controllers and use it to get/update SQL records.
My question is, since I cannot really inject these services into the main method, where would be the appropriate place to initialize the registry and add jobs for scheduling?
Thanks for the help, I am a little new to this so a little guidance would be much appreciated!
Instead of Program's Main function, I initialized the same in Startup.cs before app.UseMvc..
public void Configure(...., IDependencyObject dependencyObject)
{
....
JobManager.Initialize(new MyRegistry(dependencyObject));
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{controller}/{action}/{id?}");
});
}
My registry class looks like this:
public class MyRegistry: Registry
{
public MyRegistry(IDependencyObject dependencyObject)
{
Schedule(() => new SyncUpJob(dependencyObject)).ToRunNow().AndEvery(10).Seconds();
}
}
My Job class looks like this:
public class SyncUpJob: IJob
{
public SyncUpJob(IDependencyObject dependencyObject)
{
DependencyObject= dependencyObject;
}
public IDependencyObject DependencyObject{ get; set; }
public void Execute()
{
// call the method to run weekly here
}
}
You can define all your jobs and their schedules, by subclassing from FluentScheduler Registry class. something like:
public class JobRegistry : Registry {
public JobRegistry() {
Schedule<EmailJob>().ToRunEvery(1).Days();
Schedule<SomeOtherJob>().ToRunEvery(1).Seconds();
}
}
public class EmailJob : IJob {
public DbContext Context { get; } // we need this dependency, right?!
public EmailJob(DbContext context) //constructor injection
{
Context = context;
}
public void Execute()
{
//Job implementation code: send emails to users and update database
}
}
For injecting dependencies into jobs, you need to implement FluentScheduler IJobFactory interface. GetJobIntance method is called by FluentScheduler for creating job instances. Here you can use any DI library you want; In this sample implementation, I'm going to assume that you use Ninject:
public class MyNinjectModule : NinjectModule {
public override void Load()
{
Bind<DbContext>().To<MyDbContextImplemenation>();
}
}
public class JobFactory : IJobFactory {
private IKernel Kernel { get; }
public JobFactory(IKernel kernel)
{
Kernel = kernel;
}
public IJob GetJobInstance<T>() where T : IJob
{
return Kernel.Get<T>();
}
}
Now you can start your jobs in main method by calling:
JobManager.JobFactory = new JobFactory(new StandardKernel(new MyNinjectModule()));
JobManager.Initialize(new JobRegistry());