Skip to content

Commit

Permalink
feat: add simple rule for writting test
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Jul 27, 2023
1 parent 49fc716 commit 5f38256
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 32 deletions.
25 changes: 25 additions & 0 deletions docs/autodev/test-prompt.md
@@ -0,0 +1,25 @@
Write unit test for following code.
You MUST return method code only, not java class, no explain.
You MUST use given-when-then style.
You MUST use should_xx style for test method name.
When testing controller, you MUST use MockMvc and test API only.
You MUST return start with @Test annotation.
You are working on a project that uses Spring MVC,Spring WebFlux,JDBC to build RESTful APIs.
// class name: BookMeetingRoomRequest
// class fields: meetingRoomId
// class methods:
// super classes: [Object]
//
// class name: BookMeetingRoomResponse
// class fields: bookingId meetingRoomId userId startTime endTime
// class methods:
// super classes: [Object]
```java
@PostMapping("/{meetingRoomId}/book")
public ResponseEntity<BookMeetingRoomResponse> bookMeetingRoom(@PathVariable String meetingRoomId, @RequestBody BookMeetingRoomRequest request) {
// 业务逻辑
BookMeetingRoomResponse response = new BookMeetingRoomResponse();
// 设置 response 的属性
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
```
Expand Up @@ -4,6 +4,7 @@ import cc.unitmesh.devti.context.ClassContext
import cc.unitmesh.devti.context.ClassContextProvider
import cc.unitmesh.devti.provider.TestContextProvider
import cc.unitmesh.devti.provider.TestFileContext
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.command.WriteCommandAction
Expand Down Expand Up @@ -139,46 +140,39 @@ class JavaTestContextProvider : TestContextProvider() {
return insertClassCode(sourceFile, project, code)
}

return runWriteAction {
// Check if the root element (usually a class) of the source file is PsiClass
val rootElement = sourceFile.children.find { it is PsiClass } as? PsiClass ?: return@runWriteAction false
val rootElement = runReadAction {
sourceFile.children.find { it is PsiClass } as? PsiClass
} ?: return false

// Create the new test method
ApplicationManager.getApplication().invokeLater {
val psiElementFactory = PsiElementFactory.getInstance(project)

val newTestMethod = psiElementFactory.createMethodFromText(code, rootElement)

// Check if the method already exists in the class
if (rootElement.findMethodsByName(newTestMethod.name, false).isNotEmpty()) {
log.error("Method already exists in the class: ${newTestMethod.name}")
return@runWriteAction false
}

// Add the @Test annotation if it's missing
val modifierList: PsiModifierList = newTestMethod.modifierList
val testAnnotation: PsiAnnotation = psiElementFactory.createAnnotationFromText("@Test", newTestMethod)
modifierList.add(testAnnotation)

// Insert the new test method into the class
val addedMethod: PsiMethod = rootElement.add(newTestMethod) as PsiMethod
log.info("newTestMethod: ${newTestMethod.text}")

// Format the newly inserted code
addedMethod.navigate(true)
WriteCommandAction.runWriteCommandAction(project) {
val addedMethod: PsiMethod = rootElement.add(newTestMethod) as PsiMethod
addedMethod.navigate(true)
}

// Refresh the project to make the changes visible
project.guessProjectDir()?.refresh(true, true)

return@runWriteAction true
}

return true
}

override fun insertClassCode(sourceFile: PsiFile, project: Project, code: String): Boolean {
val psiTestFile = PsiManager.getInstance(project).findFile(sourceFile.virtualFile) ?: return false

WriteCommandAction.runWriteCommandAction(project) {
// add code to test file by string
val document = psiTestFile.viewProvider.document!!
document.insertString(document.textLength, code)
ApplicationManager.getApplication().invokeLater {
WriteCommandAction.runWriteCommandAction(project) {
val document = psiTestFile.viewProvider.document!!
document.insertString(document.textLength, code)
}
}

return true
Expand All @@ -192,7 +186,6 @@ class JavaTestContextProvider : TestContextProvider() {
val testFile = testDir.createChildData(this, testFileName)
testFile.setBinaryContent(testFileContent.toByteArray())

// Refresh the test directory to make sure the test file is visible
testDir.refresh(false, true)

return testFile
Expand Down
25 changes: 17 additions & 8 deletions src/main/kotlin/cc/unitmesh/devti/intentions/WriteTestIntention.kt
Expand Up @@ -10,6 +10,7 @@ import cc.unitmesh.devti.provider.context.ChatContextProvider
import cc.unitmesh.devti.provider.context.ChatCreationContext
import cc.unitmesh.devti.provider.context.ChatOrigin
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
Expand Down Expand Up @@ -53,13 +54,16 @@ class WriteTestIntention : AbstractChatIntention() {
| You MUST return code only, not explain.
| You MUST use given-when-then style.
| You MUST use should_xx style for test method name.
| When testing controller, you MUST use MockMvc and test API only.
| """.trimMargin()
} else {
"""Write unit test for following code.
| You MUST return method code only, not java class, no explain.
| You MUST use given-when-then style.
| You MUST use should_xx style for test method name.
| """
| When testing controller, you MUST use MockMvc and test API only.
| You MUST return start with @Test annotation.
| """.trimMargin()
}

val creationContext = ChatCreationContext(ChatOrigin.Intention, actionType, file)
Expand All @@ -68,6 +72,8 @@ class WriteTestIntention : AbstractChatIntention() {
prompter += it.text
}

prompter += "\n"

val additionContext = testContext.relatedClass.joinToString("\n") {
it.toQuery()
}.lines().joinToString("\n") {
Expand All @@ -76,10 +82,11 @@ class WriteTestIntention : AbstractChatIntention() {

prompter += additionContext

prompter += """```$lang
|$selectedText
|```
|"""
prompter += "```$lang\n$selectedText\n```\n"

if (!testContext.isNewFile) {
prompter += "Start writing test method code here: \n"
}

// navigate to the test file
navigateTestFile(testContext.file, editor, project)
Expand All @@ -92,9 +99,11 @@ class WriteTestIntention : AbstractChatIntention() {
suggestion.append(it)
}

parseCodeFromString(suggestion.toString()).forEach {
val testFile: PsiFile = PsiManager.getInstance(project).findFile(testContext.file)!!
testContextProvider.insertTestMethod(testFile, project, it)
runReadAction {
parseCodeFromString(suggestion.toString()).forEach {
val testFile: PsiFile = PsiManager.getInstance(project).findFile(testContext.file)!!
testContextProvider.insertTestMethod(testFile, project, it)
}
}
}
}
Expand Down

0 comments on commit 5f38256

Please sign in to comment.