Running a specific #Issue via gradle - java

I am using Spock to write tests and Gradle to run them. I annotate them using #Issue. Tests for the same issue are not necessarily in the same file:
FooTest.groovy:
class FooTest extends Specification {
#Issue("FOOBAR-123")
def "should foo"() {
...
}
#Issue("FOOBAR-234")
def "should bar"() {
...
}
}
BarTest.groovy:
class BarTest extends Specification {
#Issue("FOOBAR-123")
def "should quux"() {
...
}
}
I would like to run all tests for a single issue (FOOBAR-123).
In rspec it would be easy:
describe 'foo' do
it "should foo", foobar-123: true do
...
end
rspec --tag foobar-123
But I can't see how to do this with Spock and Gradle.

This is possible through the use of extensions. You can do that by creating a file in your project
IssueIncludeExtension.groovy
package com.tarun.lalwani
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.FeatureInfo
import org.spockframework.runtime.model.SpecInfo
import spock.lang.Issue
import java.lang.annotation.Annotation
class IssueIncludeExtension extends AbstractGlobalExtension{
#Override
void visitSpec(SpecInfo spec) {
def issuesList
issuesList = System.properties["spock.issues"]
if (issuesList) {
def arrIssues = issuesList.split(",").toList()
System.out.println('I was called')
for (FeatureInfo feature : spec.getAllFeatures())
{
def method, ann;
method = feature.getFeatureMethod();
def issueAnnotation = method.getAnnotation(Issue.class);
if (issueAnnotation) {
if (issueAnnotation.value().size() > 0)
{
if (issueAnnotation.value().toList().intersect(arrIssues))
{
//we have a matching issue
feature.setExcluded(false)
} else {
feature.setExcluded((true))
}
}
} else {
// this doesn't belong to any issue
feature.setExcluded(true)
}
}
} else {
super.visitSpec(spec)
}
}
}
Then in create below file
META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
com.tarun.lalwani.IssueIncludeExtension
After that you can update your gradle script and add a test
task issueTest(type: Test) {
// This task belongs to Verification task group.
group = 'Verification'
// Set Spock configuration file when running
// this test task.
systemProperty 'spock.issues', 'Issue4'
}
Now I have below tests in groovy
package com.mrhaki.spock
import spock.lang.Issue
import spock.lang.Specification
class WordRepositorySpec extends Specification {
#Remote // Apply our Remote annotation.
#Issue(["Issue1", "Issue2"])
def "test remote access"() {
given:
final RemoteAccess access = new RemoteAccess()
expect:
access.findWords('S') == ['Spock']
}
#Issue("Issue4")
def "test local access"() {
given:
final LocalAccess access = new LocalAccess()
expect:
access.findWords('S') == ['Spock']
}
}
Running the test just runs Issue4 related test

According to this article, you may create a configuration for any annotation.
Unfortunately (after some tries) I wasn't able to filter tests against a value of the annotation, the IncludeExcludeCriteria class only accepts classes or annotations.
runner {
include spock.lang.Issue
}
I think you might get around it with creating annotations per issue and using Annotations Collectors.

Related

ArchUnit test importOptions DoNotIncludeTests not working

I'm using ArchUnit 0.18 and archunit-junit5 from a Spring 2.3.8 application. For some reason I can't find, ImportOption.DoNotIncludeTests.class is not working as expected and test classes are also included. I can only get the ArchUnit test working by commenting the code in those tests classes.
ArchUnit test is:
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import com.tngtech.archunit.junit.AnalyzeClasses;
import org.junit.jupiter.api.Test;
#AnalyzeClasses(packages = "com.petproject", importOptions = { ImportOption.DoNotIncludeTests.class })
class ArchitectureTest {
#Test
public void some_architecture_rule() {
JavaClasses classes = new ClassFileImporter().importPackages("com.petproject");
layeredArchitecture()
.layer("Controller").definedBy("com.petproject.controller")
.layer("Validators").definedBy("com.petproject.validations.validators")
.layer("Service").definedBy("com.petproject.service")
.layer("Persistence").definedBy("com.petproject.repository")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller", "Validators")
.check(classes);
}
}
Am I missing some step in order to not get into account test classes?
Thanks!
The annotation #AnalyzeClasses(packages = "com.petproject", importOptions = { ImportOption.DoNotIncludeTests.class }) is not evaluated in this case. First, the test method is annotated with JUnit's #Test instead of ArchUnit's #ArchTest. Second, the rule is checked against the result of
JavaClasses classes = new ClassFileImporter().importPackages("com.petproject");
As you already figured out, you could replace this with
JavaClasses classes = new ClassFileImporter().withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS).importPackages("com.petproject");
Alternatively you could change your test setup to
#AnalyzeClasses(packages = "com.petproject", importOptions = { ImportOption.DoNotIncludeTests.class })
class ArchitectureTest {
#ArchTest
public static void some_architecture_rule(JavaClasses classes) {
yourCreatedRule.check(classes);
}
// or as a field instead of a method
#ArchTest
public static ArchRule some_architecture_rule = ...
}

No code coverage when using PowerMock to test final class with static methods Java

I am writing a test case for a final class that has static methods defined inside it.
Now the test case works fine , but when I click on code coverage it says 0% , although the test case runs fine.
I am using PowerMock along with EasyMock and using Junit4.
My class
public class Meals {
public static String getName(String name) {
if (name == null) {
return "bad";
} else {
return "good";
}
}
}
And My test case
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Meals.class)
public class MealsTest {
#Before
public void setUp() throws Exception {
PowerMock.mockStatic(Meals.class);
}
#Test
public void testMeals() {
EasyMock.expect(Meals.getName(null)).andReturn("bad");
PowerMock.replayAll();
assertEquals("bad", Meals.getName(null));
PowerMock.verifyAll();
}
}
According to me it is because of the annotations Preparefortest and RunWith , but i'm not sure.
So any help would be appreciated .
PowerMock is known to kill code coverage. I think it's because they load custom classes in a special class loader and this code isn't instrumented by the code coverage tools because it's after the fact. I don't think these is a solution to that apart from no using PowerMock.
Do we agree that you test is currently not testing anything? It's just test that PowerMock is working.
If you are using jacoco + Gradle, you can enable offline-instrumentation by following
task instrument(dependsOn: [classes, project.configurations.jacocoAnt]) {
inputs.files classes.outputs.files
File outputDir = new File(project.buildDir, 'instrumentedClasses')
outputs.dir outputDir
doFirst {
project.delete(outputDir)
ant.taskdef(
resource: 'org/jacoco/ant/antlib.xml',
classpath: project.configurations.jacocoAnt.asPath,
uri: 'jacoco'
)
def instrumented = false
if (file(sourceSets.main.java.outputDir).exists()) {
def instrumentedClassedDir = "${outputDir}/${sourceSets.main.java}"
print sourceSets.main.java.outputDir
ant.'jacoco:instrument'(destdir: instrumentedClassedDir) {
fileset(dir: sourceSets.main.java.outputDir, includes: '**/*.class')
}
//Replace the classes dir in the test classpath with the instrumented one
sourceSets.test.runtimeClasspath -= files(sourceSets.main.java.outputDir)
sourceSets.test.runtimeClasspath += files(instrumentedClassedDir)
instrumented = true
}
if (instrumented) {
test.jvmArgs += '-noverify'
}
}
}
test {
dependsOn instrument
useJUnit()
finalizedBy jacocoTestReport // report is always generated after tests run
}
Code credits to https://github.com/esdk/g30l0/commit/82af4c9aad50aadc40d940471fe1b934473170c7
This is an open issue
https://github.com/gradle/gradle/issues/2429

Mockito mocks locally final class but fails in Jenkins

I have written some unit tests for a static method. The static method takes only one argument. The argument's type is a final class. In terms of code:
public class Utility {
public static Optional<String> getName(Customer customer) {
// method's body.
}
}
public final class Customer {
// class definition
}
So for the Utility class I have created a test class UtilityTests in which I have written tests for this method, getName. The unit testing framework is TestNG and the mocking library that is used is Mockito. So a typical test has the following structure:
public class UtilityTests {
#Test
public void getNameTest() {
// Arrange
Customer customerMock = Mockito.mock(Customer.class);
Mockito.when(...).thenReturn(...);
// Act
Optional<String> name = Utility.getName(customerMock);
// Assert
Assert.assertTrue(...);
}
}
What is the problem ?
Whereas the tests run successfully locally, inside IntelliJ, they fail on Jenkins (when I push my code in the remote branch, a build is triggered and unit tests run at the end). The error message is sth like the following:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.packagename.Customer Mockito
cannot mock/spy because :
- final class
What I tried ?
I searched a bit, in order to find a solution but I didn't make it. I note here that I am not allowed to change the fact that Customer is a final class. In addition to this, I would like if possible to not change it's design at all (e.g. creating an interface, that would hold the methods that I want to mock and state that the Customer class implements that interface, as correctly Jose pointed out in his comment). The thing that I tried is the second option mentioned at mockito-final. Despite the fact that this fixed the problem, it brake some other unit tests :(, that cannot be fixed in none apparent way.
Questions
So here are the two questions I have:
How that is possible in the first place ? Shouldn't the test fail both locally and in Jenkins ?
How this can be fixed based in the constraints I mentioned above ?
Thanks in advance for any help.
An alternative approach would be to use the 'method to class' pattern.
Move the methods out of the customer class into another class/classes, say CustomerSomething eg/CustomerFinances (or whatever it's responsibility is).
Add a constructor to Customer.
Now you don't need to mock Customer, just the CustomerSomething class! You may not need to mock that either if it has no external dependencies.
Here's a good blog on the topic: https://simpleprogrammer.com/back-to-basics-mock-eliminating-patterns/
How that is possible in the first place? Shouldn't the test fail both locally and in Jenkins ?
It's obviously a kind of env-specifics. The only question is - how to determine the cause of difference.
I'd suggest you to check org.mockito.internal.util.MockUtil#typeMockabilityOf method and compare, what mockMaker is actually used in both environments and why.
If mockMaker is the same - compare loaded classes IDE-Client vs Jenkins-Client - do they have any difference on the time of test execution.
How this can be fixed based in the constraints I mentioned above?
The following code is written in assumption of OpenJDK 12 and Mockito 2.28.2, but I believe you can adjust it to any actually used version.
public class UtilityTest {
#Rule
public InlineMocksRule inlineMocksRule = new InlineMocksRule();
#Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
#Test
public void testFinalClass() {
// Given
String testName = "Ainz Ooal Gown";
Client client = Mockito.mock(Client.class);
Mockito.when(client.getName()).thenReturn(testName);
// When
String name = Utility.getName(client).orElseThrow();
// Then
assertEquals(testName, name);
}
static final class Client {
final String getName() {
return "text";
}
}
static final class Utility {
static Optional<String> getName(Client client) {
return Optional.ofNullable(client).map(Client::getName);
}
}
}
With a separate rule for inline mocks:
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.util.MockUtil;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class InlineMocksRule implements TestRule {
private static Field MOCK_MAKER_FIELD;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class);
MOCK_MAKER_FIELD = MockUtil.class.getDeclaredField("mockMaker");
MOCK_MAKER_FIELD.setAccessible(true);
int mods = MOCK_MAKER_FIELD.getModifiers();
if (Modifier.isFinal(mods)) {
modifiers.set(MOCK_MAKER_FIELD, mods & ~Modifier.FINAL);
}
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
Object oldMaker = MOCK_MAKER_FIELD.get(null);
MOCK_MAKER_FIELD.set(null, Plugins.getPlugins().getInlineMockMaker());
try {
base.evaluate();
} finally {
MOCK_MAKER_FIELD.set(null, oldMaker);
}
}
};
}
}
Make sure you run the test with the same arguments. Check if your intellij run configurations match the jenkins. https://www.jetbrains.com/help/idea/creating-and-editing-run-debug-configurations.html. You can try to run test on local machine with the same arguments as on jenkins(from terminal), if it will fail that means the problem is in arguments

Testing Dropwizard 'Application' class

I'm looking to test the Dropwizard 'Application' class without bootstrapping an entire Dropwizard server.
I'd essentially just like to ensure that the one bundle I'm registering is registered successfully.
All the routes I've been down so far result in NullPointer exceptions due to various other components not being setup correctly. Is there an easy path here?
public class SentimentApplication extends Application<SentimentConfiguration> {
public static void main(final String[] args) throws Exception {
new SentimentApplication().run(args);
}
#Override
public String getName() {
return "Sentiment";
}
#Override
public void initialize(final Bootstrap<SentimentConfiguration> bootstrap) {
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor(false)
)
);
}
#Override
public void run(final SentimentConfiguration configuration,
final Environment environment) {
// TODO: implement application
}
}
You can register a simple command and call run method of your application with that command instead of server command. That way your application will be executed without running a server.
I wanted to do sth similar to what you want. (Considering ExampleApp as main Application class of my code) I wanted to write a test to make sure that there is no exception parsing the configuration. (Because KotlinModule() should have beed registered to environment.objectMaooer in initialize method of app, otherwise we would have had a runtime error.) I achieved it with sth similar to:
import io.dropwizard.cli.EnvironmentCommand
import io.dropwizard.setup.Bootstrap
import io.dropwizard.setup.Environment
import com.example.config.ExampleConfiguration
import net.sourceforge.argparse4j.inf.Namespace
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class DummyCommand(app: DummyApp) : EnvironmentCommand<ExampleConfiguration>(app, "dummy", "sample test cmd") {
var parsedConfig: ExampleConfiguration? = null
override fun run(environment: Environment, namespace: Namespace, configuration: ExampleConfiguration) {
parsedConfig = configuration
}
}
class DummyApp : ExampleApp() {
val cmd: DummyCommand by lazy { DummyCommand(this) }
override fun initialize(bootstrap: Bootstrap<ExampleConfiguration>) {
super.initialize(bootstrap)
bootstrap.addCommand(cmd)
}
}
class ExampleAppTest {
#Test
fun `Test ExampleConfiguration is parsed successfully`() {
val app = DummyApp()
app.run("dummy", javaClass.getResource("/example-app-test/test-config.yml").file)
val config = app.cmd.parsedConfig
assertNotNull(config)
assertEquals("foo", config.nginxUsername)
}
}

How can I run all JUnit unit tests except those ending in "IntegrationTest" in my IntelliJ IDEA project using the integrated test runner?

I basically want to run all JUnit unit tests in my IntelliJ IDEA project (excluding JUnit integration tests), using the static suite() method of JUnit. Why use the static suite() method? Because I can then use IntelliJ IDEA's JUnit test runner to run all unit tests in my application (and easily exclude all integration tests by naming convention). The code so far looks like this:
package com.acme;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class AllUnitTests extends TestCase {
public static Test suite() {
List classes = getUnitTestClasses();
return createTestSuite(classes);
}
private static List getUnitTestClasses() {
List classes = new ArrayList();
classes.add(CalculatorTest.class);
return classes;
}
private static TestSuite createTestSuite(List allClasses) {
TestSuite suite = new TestSuite("All Unit Tests");
for (Iterator i = allClasses.iterator(); i.hasNext();) {
suite.addTestSuite((Class<? extends TestCase>) i.next());
}
return suite;
}
}
The method getUnitTestClasses() should be rewritten to add all project classes extending TestCase, except if the class name ends in "IntegrationTest".
I know I can do this easily in Maven for example, but I need to do it in IntelliJ IDEA so I can use the integrated test runner - I like the green bar :)
How about putting each major group of junit tests into their own root package. I use this package structure in my project:
test.
quick.
com.acme
slow.
com.acme
Without any coding, you can set up IntelliJ to run all tests, just the quick ones or just the slow ones.
I've written some code to do most of the work. It works only if your files are on the local disk instead of in a JAR. All you need is one class in the package. You could, for this purpose, create a Locator.java class, just to be able to find the package.
public class ClassEnumerator {
public static void main(String[] args) throws ClassNotFoundException {
List<Class<?>> list = listClassesInSamePackage(Locator.class, true);
System.out.println(list);
}
private static List<Class<?>> listClassesInSamePackage(Class<?> locator, boolean includeLocator)
throws ClassNotFoundException {
File packageFile = getPackageFile(locator);
String ignore = includeLocator ? null : locator.getSimpleName() + ".class";
return toClassList(locator.getPackage().getName(), listClassNames(packageFile, ignore));
}
private static File getPackageFile(Class<?> locator) {
URL url = locator.getClassLoader().getResource(locator.getName().replace(".", "/") + ".class");
if (url == null) {
throw new RuntimeException("Cannot locate " + Locator.class.getName());
}
try {
return new File(url.toURI()).getParentFile();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private static String[] listClassNames(File packageFile, final String ignore) {
return packageFile.list(new FilenameFilter(){
#Override
public boolean accept(File dir, String name) {
if (name.equals(ignore)) {
return false;
}
return name.endsWith(".class");
}
});
}
private static List<Class<?>> toClassList(String packageName, String[] classNames)
throws ClassNotFoundException {
List<Class<?>> result = new ArrayList<Class<?>>(classNames.length);
for (String className : classNames) {
// Strip the .class
String simpleName = className.substring(0, className.length() - 6);
result.add(Class.forName(packageName + "." + simpleName));
}
return result;
}
}
What about using JUnit4 and the Suite-Runner?
Example:
#RunWith(Suite.class)
#Suite.SuiteClasses({
UserUnitTest.class,
AnotherUnitTest.class
})
public class UnitTestSuite {}
I made a small Shell-Script to find all Unit-Tests and another one to find my Integration-Tests. Have a look at my blog entry:
http://blog.timomeinen.de/2010/02/find-all-junit-tests-in-a-project/
If you use Spring TestContext you can use the #IfProfile Annotation to declare different tests.
Kind regards,
Timo Meinen
Spring has implemented an excellent classpath search function in the PathMatchingResourcePatternResolver. If you use the classpath*: prefix, you can find all the resources, including classes in a given hierarchy, and even filter them if you want. Then you can use the children of AbstractTypeHierarchyTraversingFilter, AnnotationTypeFilter and AssignableTypeFilter to filter those resources either on class level annotations or on interfaces they implement.
http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html
http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/core/type/filter/AbstractTypeHierarchyTraversingFilter.html
Solution: https://github.com/MichaelTamm/junit-toolbox
Use the following features
#RunWith(WildcardPatternSuite.class)
#SuiteClasses({"**/*.class", "!**/*IntegrationTest.class"})
public class AllTestsExceptionIntegrationSuit {
}
assuming you following a naming pattern where you integration tests end in ...IntegrationTest and you place the file in the top-most package (so the **/*.class search will have the opportunity to pick up all your tests)

Categories

Resources