Setting up JUnit4

This post is mainly aimed at students taking a course in (imperative and) object-oriented programming methodology at Uppsala University. But the content covered isn't specific to that course, just the examples. Please send any errata over to me and you'll get a gold star!

I see you want to start writing tests for your code, great choice! Hopefully you haven't been putting of this step for too long (if you did that's okay as well, just be extra attentive writing your tests).

Exclamating hyena
Tests! (this should be your reaction) Sticker by NaL (Will fix image as soon as I'm done migrating)

There are multiple ways of getting Java to find the files necessary to write JUnit tests. To make this guide as general as possible we'll download JUnit to a folder close to your project repo.

Danger! Before you start running these commands, take a look through them and make sure they aren't spoopy. You generally shouldn't trust strangers on the internet!

# Assuming you're starting in ./firstname.lastname.number/fas2/inluppX/ 
mkdir lib 
cd lib 

# Download the libraries 
curl -L https://search.maven.org/remotecontent?filepath=junit/junit/4.13.1/junit-4.13.1.jar --output junit.jar 
curl -L https://search.maven.org/remotecontent?filepath=org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar --output hamcrest-core.jar
Create a library folder and download the JUnit

Next step is to actually create a few tests. If you remember working with CUnit in phase 1 you'll be in for a pleasant surprise! JUnit handles a lot of the busywork managing testsuites by using Java annotations before the tests. An example of a Java annotation is @Override that you've used before. Anyways, let's get back on track.

At this point you should have a few packages in your symbolic calculator, such as ast and parser. You can choose to add your tests to them, or you can create a new package. This affects what you can and cannot access in your classes. For example, if you haven't specified access level modifiers to your attributes you can still access them if you're in the same package[0]. We're going with the second route, I'll leave it to you to think about the reason why.

So! Let's create a new class for a few AST tests. I'll leave it to you to populate them with actual test-code.

// ASTTests.java 
package org.ioopm.calculator.tests; // Important! 

import org.junit.Test; 
import static org.junit.Assert.*; 

public class ASTTests { 
    @Test 
    public void SimpleTest() { 
        String s = "Hewwo, world"; 
        assertEquals("Hewwo, world", s); 
    } 
}
A common mistake is to import junit.framework.TestCase, you don't need to do that!

Okay, now over to the last step. Now we want to modify the makefile that you should've created so that it finds JUnit. This step will depend a bit on how it looks right now, but most importantly you need to add the correct -cp flag to javac when you're compiling and running the tests. I've seen issues where it (semi-)silently fails if the wrong path is added, so really make sure it's correct!

# This is not a good makefile, it doesn't define 
# dependencies so it always recompiles. You can 
# probably sneak it by the TA's, but they'd be 
# impressed if you actually write a good 
# makefile during the Java phase!

compile_tests:
	javac -cp .:lib/hamcrest-core.jar:lib/junit.jar *.java

run_tests: compile_tests
	java -cp .:classes:lib/hamcrest-core.jar:lib/junit.jar org.junit.runner.JUnitCore org.ioopm.calculator.tests.ASTTests

See the -cp flag? It tells Java where to find all the necessary classes. When compiling and running, it needs to find JUnit and Hamcrest, when running them we also need to point it to the classes folder where all the .class files go.

That's it! Now you should have working tests! You can spice things up a bit by adding a method with the annotation @Before that will run before each test. Super useful to instantiate objects you need in every test (such as Constants).

Common Issues

These are the most common issues I've seen people struggling with when setting up JUnit the last couple of years.

assertEquals(double, double) is deprecated

Remember Computer Architecture? Floating point precision is a common problem, and comparing to float values can be dangerous. There's a new method that takes a 3rd argument - delta. It specifies the maximum difference between the two floats. Use that, or use your toString() method and compare the strings instead.

Test is not an annotation / it is a class/method

The assignment throws a small curveball! The file Test.java containing some system level tests has the same name as the annotation @Test. Try renaming the file and the class to something more descriptive and it should fix the issue.

Footnotes

[0] BTW! This is a perfect time to look into the achievement about access modifiers. I'd recommend starting in the Java documentation.