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 = ...
}
Related
I am trying to use power mock in scala.
Class I want to test
class Operations {
def write(s: String): Unit = {
val customWriter = new CustomWriter()
customWriter.write(s)
}
}
Class I want to mock
class CustomWriter {
def write(s: String): Unit = {
//do something
}
}
My spec file
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.scalatest.mockito.MockitoSugar.mock
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.scalatest.Matchers._
import org.scalatest.{BeforeAndAfterAll, FunSpec, PrivateMethodTester}
#RunWith(classOf[PowerMockRunner])
#PrepareForTest(Array(classOf[Operations]))
class PowerMockTest extends FunSpec {
describe("Using power mock") {
it("check constructor call") {
val ops = new Operations()
val mocked = mock[CustomWriter]
PowerMockito.whenNew(classOf[CustomWriter]).withNoArguments().thenReturn(mocked)
ops.write("Write Me")
PowerMockito.verifyNew(classOf[CustomWriter]).withNoArguments()
}
}
}
How ever the tests are failing with the stacktrace
java.lang.AssertionError: Wanted but not invoked customWriter();
Actually, there were zero interactions with this mock.
I even tried verifying the method call on mocked object, still no luck
Mockito.verify(mocked).write("Write me")
Please help me understand what am I missing here
Looks like you missing CustomWriter from the #PrepareForTest annotation.
The need to use Powermock can be a sing that you're doing something wrong. Maybe you can change your approach so that you can test it with Mockito.
If you have control over the Operations class, you can maybe pass the CustomWriter instance instead of instantiating it inside the method.
I have a strange problem. I've written my API and I just wanted to test it.
I wrote a simple test code by using Restassured:
package com.example.restservicetest;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import java.util.*;
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RestServiceTestApplicationTests {
#LocalServerPort
private int port;
private Response response;
private List<Object> folderList;
#Test
public void getWholeFolders() {
response = RestAssured.given().port(port).when().get("/api/opening").then().statusCode(200).extract().response();
folderList= response.jsonPath().getList("folders");
}
#Test
public void getRandomFolderNumber() {
Random rand = new Random();
RestAssured.given().port(port).when().get("/api/opening/folder/" + rand.nextInt(folderList.size()-1)).then().statusCode(200);
}
}
When I debug my test, at the end of first getWholeFolders test, I see that folderList is not empty as I expected. My whole folder list is assigning to it.
But when the time is for 2nd test getRandomFolderNumber, I see that folderList becomes null.
Why does it become null?
As Andriy mentiond in comment, JUnit Jupiter will use a default lifecycle mode, it means JUnit creates a new instance of each test class before executing each test method TestInstance.Lifecycle.PER_METHOD.
To change that, you change PER_METHOD --> PER_CLASS (basically means one instance of test class for all test method).
One more subtle thing, you need to set order for test method to make sure getRandomFolderNumber always run after getWholeFolders
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class RestServiceTestApplicationTests {
...
#Test
#Order(1)
public void getWholeFolders() {
...
}
#Test
#Order(2)
public void getRandomFolderNumber() {
...
}
}
As I understand that if we use spring stereotypes then we don't need to use new keyword to create an instance. Spring manages that for us and provide us with the beans at runtime.
And in order for Spring to inject those beans we need to use #Autowired annotation where we want Spring to inject that bean.
Below I have a very simple class where I am using #Component so that spring manages that. This class has one List which I am initializing with my own responsibility and then a small method which does some logic.
#Slf4j
#Data
#NoArgsConstructor
#AllArgsConstructor
#Component
public class Parser {
private List<String> strList = new ArrayList<>();
public void parseStrings(final String[] strs) {
Arrays.stream(strs)
.map(String::toLowerCase)
.filter(str -> str.length() > 8)
.filter(str -> str.endsWith("sam"))
.forEach(sam1 -> { strList.add(sam1); });
}
}
I also wrote one unit test to test that and here is that.
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.jupiter.api.Assertions.*;
#RunWith(MockitoJUnitRunner.class)
class ParserTest {
#Autowired
private Parser parser;
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}
}
The test fails with
java.lang.NullPointerException when it tries to call parseStrings method which means that its not able to inject a proper initialized bean at run time.
Can some one guide that what I am missing?
Is it necessary to add constructors (which here I am doing using lombok annotations) when using spring stereotypes on a class.
I don't see any mock created so why you are using #RunWith(MockitoJUnitRunner.class)?
I've seen as well answers recommending the use of #SpringBooTest. This annotation loads the whole context of your application basically for integration tests in order to integrate different layers of the application. That also means no mocking is involved. Do you really need that? (I don't think so since you're talking about unit test)
If your parser doesn't reference any other Bean (which need to be mocked), then you are in case of simple unit test.
#RunWith(SpringRunner.class) // you can even removed it
class ParserTest {
private Parser parser;
#Before
public void setUp() {
parser = new Parser();
}
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}
Spring Autowire if you run the test case with SpringRunner. So modify the test class as follows.
#RunWith(SpringRunner.class)
class ParserTest {
}
To answer your second question,
No, it is not necessary to add no-argument constructor unless you also have a parameterised constructor in the same class. In that case you need to explicitly add a no-arg constructor.
Why so you even need MockitoJUnitRunner here? The Parser has no dependencies. A simple initialization in the test will be enough. Just inialize the Parser instead of using an annotation. #SpringBootTest is meant for integration tests. It brings in the Spring context and makes your unit test slow & bulky.
In my case the class was not declared public
This should work:
#SpringBootTest
#RunWith(SpringRunner.class)
class ParserTest {
#Autowired
private Parser parser;
#Test
void parseStrings() {
String str[] = {"abcsamsam", "abcsyjhgfed abdul sam","abcAhgbkgdjhul samad", "abcabjhgdulsamsam", "sa"};
parser.parseStrings(str);
assertTrue(parser.getStrList().size() == 3);
assertTrue(parser.getStrList().get(0).equalsIgnoreCase("abcsamsam"));
}
}
SpringBoot2,you can use the annotation:#SpringBootTest to do unit test.
just like below case:
#SpringBootTest
class DemoApplicationTests {
#Test
void contextLoads() {
}
}
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
I have switched to JUnit4.4 from JUnit3.8. I run my tests using ant, all my tests run successfully but test utility classes fail with "No runnable methods" error. The pattern I am using is to include all classes with name *Test* under test folder.
I understand that the runner can't find any method annotated with #Test attribute. But they don't contain such annotation because these classes are not tests.
Surprisingly when running these tests in eclipse, it doesn't complain about these classes.
In JUnit3.8 it wasn't a problem at all since these utility classes didn't extend TestCase so the runner didn't try to execute them.
I know I can exclude these specific classes in the junit target in ant script. But I don't want to change the build file upon every new utility class I add. I can also rename the classes (but giving good names to classes was always my weakest talent :-) )
Is there any elegant solution for this problem?
Annotate your util classes with #Ignore. This will cause JUnit not to try and run them as tests.
My specific case has the following scenario. Our tests
public class VenueResourceContainerTest extends BaseTixContainerTest
all extend
BaseTixContainerTest
and JUnit was trying to run BaseTixContainerTest. Poor BaseTixContainerTest was just trying to setup the container, setup the client, order some pizza and relax... man.
As mentioned previously, you can annotate the class with
#Ignore
But that caused JUnit to report that test as skipped (as opposed to completely ignored).
Tests run: 4, Failures: 0, Errors: 0, Skipped: 1
That kind of irritated me.
So I made BaseTixContainerTest abstract, and now JUnit truly ignores it.
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
Assuming you're in control of the pattern used to find test classes, I'd suggest changing it to match *Test rather than *Test*. That way TestHelper won't get matched, but FooTest will.
To prevent JUnit from instantiating your test base class just make it
public abstract class MyTestBaseClass { ... whatever... }
(#Ignore reports it as ignored which I reserve for temporarily ignored tests.)
If this is your base test class for example AbstractTest and all your tests extends this then define this class as abstract
If it is Util class then better remove *Test from the class rename it is MyTestUtil or Utils etc.
Be careful when using an IDE's code-completion to add the import for #Test.
It has to be import org.junit.Test and not import org.testng.annotations.Test, for example. If you do the latter, you'll get the "no runnable methods" error.
Ant now comes with the skipNonTests attribute which was designed to do exactly what you seem to be looking for. No need to change your base classes to abstract or add annotations to them.
What about adding an empty test method to these classes?
public void avoidAnnoyingErrorMessageWhenRunningTestsInAnt() {
assertTrue(true); // do nothing;
}
In your test class if wrote import org.junit.jupiter.api.Test; delete it and write import org.junit.Test; In this case it worked me as well.
I was also facing a similar issue ("no runnable methods..") on running the simplest of simple piece of code (Using #Test, #Before etc.) and found the solution nowhere. I was using Junit4 and Eclipse SDK version 4.1.2. Resolved my problem by using the latest Eclipse SDK 4.2.2. I hope this helps people who are struggling with a somewhat similar issue.
I also faced the same issue once. In my case I was running my tests using Enclosed Runner of Junit. I created a class called SharedSetup to enable common features for my 2 test classes. But somehow facing the same issue.
#RunWith(Enclosed.class)
public class StructApprovalNodeTest {
abstract static class SharedSetup {
StructApprovalNode sut;
ExecutionContext ctx = mock(ExecutionContext.class);
DTDDAOService dtd = mock(DTDDAOService.class);
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Before
public void before() throws Exception {
PowerMockito.mockStatic(ServiceHelper.class);
when(ServiceHelper.getService("dtd")).thenReturn(dtd);
when(ctx.getContextInstance()).thenReturn(mock(ContextInstance.class));
when(dtd.getLatestStructures(Matchers.anyInt(), Matchers.anyString(), Matchers.anyString())).thenReturn(
new ArrayList<Trade>());
sut = new StructApprovalNode();
spy(sut);
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
#PowerMockRunnerDelegate(Parameterized.class)
public static class ParamaterizedBatchTest extends SharedSetup {
private String batchName;
private String approvalStatus;
public ParamaterizedBatchTest(String batchName, String approvalStatus) {
this.batchName = batchName;
this.approvalStatus = approvalStatus;
}
#Parameterized.Parameters
public static Collection testValues() {
return Arrays.asList(new Object[][] {
{ "SDC_HK_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_PB_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED },
{ "SDC_FX_AUTOMATION_BATCH", Constants.APRVLSTATUS_APPROVED }
});
}
#Test
public void test1_SDCBatchSourceSystems() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem(this.batchName);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(this.approvalStatus, trade.getApprovalStatus());
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest({ ServiceHelper.class, StructApprovalNode.class })
#PowerMockIgnore("javax.management.*")
public static class NonParamaterizedBatchTest extends SharedSetup {
#Test
public void test2_PrevInvalidTrade() throws Exception {
expectedException.expect(Exception.class);
expectedException.expectMessage("External Id of STRUCTURE_TRADE cannot be changed.");
Trade trade = new Trade();
trade.setExternalId(123);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1234);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
}
#Test
public void test3_ValidPrevTrade() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals("APPROVED", trade.getApprovalStatus());
}
#Test
public void test4_ValidPrevTradeAutoApprpve() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML_AUTO_APPRV.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_LIVE);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
PrevTrade prevTrade = new PrevTrade();
prevTrade.setExternalId(1277402441);
prevTrade.setApprovalStatus(Constants.APRVLSTATUS_NOTAPPROVED);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(prevTrade);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(prevTrade.getApprovalStatus(), trade.getApprovalStatus());
}
#Test
public void test5_tradeStatusDraft() throws Exception {
Trade trade = new Trade();
String tradeXml = FileHelper.getResourceFromJar("/testdata/SDC_BATCH_TRADE_XML.xml");
trade.setTradeXml(tradeXml);
trade.setTradeDoc(XmlHelper.createDocument(trade.getTradeXml()));
trade.setStatus(Constants.STATUS_DRAFT);
trade.setSourceSystem("BATCH");
trade.setExternalId(1277402441);
when(ctx.getContextInstance().getVariable("trade")).thenReturn(trade);
when(ctx.getContextInstance().getTransientVariable("prevTrade")).thenReturn(null);
sut.execute(ctx);
PowerMockito.verifyPrivate(sut, times(1)).invoke("resetApprovalDetails", trade);
Assert.assertEquals(Constants.APRVLSTATUS_NONE, trade.getApprovalStatus());
}
}
}
To solve the issue, I just removed the public modifier from the abstract super class SharedSetup and issue is fixed for good