最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Spring Boot tests don't work on Jenkins but work on local since Spring Boot 3.4.0 - Stack Overflow

programmeradmin7浏览0评论

Current Spring Boot version: 3.4.2 TestNG version: 7.10.2 Spring test: 6.2.2 (from parent spring depedency)

After Spring Boot 3.2.0, we were getting warning that @MockBean and @SpyBean were deprecated, but we continued using them. Since Spring Boot 3.4.0 they are marked for the removal, thus we migrated them to the @MockitoBean and @MockitySpyBean accordingly. On local, everything is working, they are running, but on the Jenkins we see an error that the object passed to when() is null. I tried many solutions, no luck, here is a scenario of usage (simplified). To keep the project's NDA I refactored code, but kept the main purpose of usage:

Hierarchy: MyTest <- SomeBaseAPITest <- BaseAPITest (with custom @DefaultTestConfig annotation) <- AbstractTestNGSpringContextTests

AbstractTestNGSpringContextTests is the class from spring

Logic: BaseAPITest is responsible for context SomeBaseAPITest is responsible for mocks creating MyTest is the child for the classes above

@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootTest(classes = { Application.class },
properties = { "spring.main.allow-bean-definition-overriding=true" },
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ComponentScan({ "com.blabla.blabla" })
@TestConfiguration
public @interface DefaultTestConfig {

}
@DefaultTestConfig
public abstract class BaseAPITest extends AbstractTestNGSpringContextTests {

    @LocalServerPort
    private int port;
    
    @BeforeSuite(alwaysRun = true)
    public final void prepareTestContext() throws Exception {
       log.info("Preparing test context");
       super.springTestContextPrepareTestInstance();
    }

}
@SuppressWarnings("unchecked")
public class SomeBaseAPITest extends BaseAPITest {

    @MockitoSpyBean(reset = MockReset.NONE)
    private SomeCsvServiceImpl someCsvService;
    
    @BeforeSuite(alwaysRun = true, dependsOnMethods = "prepareTestContext")
    protected void prepareMocks() {
        doAnswer(invocationOnMock -> TestService.translate(invocationOnMock.getArgument(1)))
            .when(morningstarCsvService)
            .translate(any(), any(), any(), any(), any());
    
    }

}
public class MyTest extends SomeBaseAPITest {

    @Test
    void myTest() {
        var response = given()
        .get(";);
    
        assertTrue(response != null);
    }

}

The error:

18:11:09  [INFO]
18:11:09  [INFO] Results:
18:11:09  [INFO]
18:11:09  [ERROR] Failures:
18:11:09  [ERROR]   SomeBaseAPITest.prepareMocks:83 » NullInsteadOfMock
18:11:09  Argument passed to when() is null!
18:11:09  Example of correct stubbing:
18:11:09      doThrow(new RuntimeException()).when(mock).someMethod();
18:11:09  Also, if you use @Mock annotation don't miss openMocks()
18:11:09  [INFO]
18:11:09  [ERROR] Tests run: 357, Failures: 1, Errors: 0, Skipped: 356

The error means the code here:

doAnswer(invocationOnMock -> TestService.translate(invocationOnMock.getArgument(1)))
.when(morningstarCsvService)
.translate(any(), any(), any(), any(), any());

Why do we use such hierarchy of classes? To run the context once and reuse context in mocks configuration. Why two methods for beforeSuite? The one method loads the context (because the context is by default loaded in beforeClass, but in beforeSuite it is manually forced to load). The second one delegates creating mocks (in the real example there are more classes and more methods, here is an abstraction).

Again: it is working on local, and isn't on jenknins. Jenkins can see some error, and maybe it is correct. When hovering the mouse on the

@MockitoSpyBean(reset = MockReset.NONE)
private SomeCsvServiceImpl someCsvService;

it is showing, that someCsvService is not assigned (means the value is not initialized). When there was @MockBean annotation, there wasn't such warning. I hope to get help from the community!

Simplified Jenkins pipeline:

import hudson.util.Secret 

node('maven38'){ 
try { 
    stage('Checkout code') { 
        git(branch: '$BRANCH', credentialsId: 'GITHUB', url: 'https://NDA') 
    } 
    stage('Run Test') { 
        withCredentials([usernamePassword(credentialsId: 'DEVOPS', passwordVariable: 'NEXUS_PASS', usernameVariable: 'NEXUS_USER')]) { 
        sh ''' mvn --settings ${WORKSPACE}/settings.xml -Dspring.profiles.active=test -Dsurefire.suiteXmlFiles=src/test/resources/suites/api-testng.xml clean test ''' 
        } 
    } 
} catch (e) { 
    echo 'This will run only if failed' throw e 
} finally { ...

I tried changing private to protected, tried to move field to the test classes, tried to change the configuration with different combinations. I cannot migrate to the @MockBean to test as it is marked for removal (of course I can suppress removal warnings, but that's not a solution)

I expect to see no error with null instead of object in mock configuration on Jenkins.

发布评论

评论列表(0)

  1. 暂无评论